博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
POJ 3345 Bribing FIPA(树形DP)
阅读量:7088 次
发布时间:2019-06-28

本文共 5022 字,大约阅读时间需要 16 分钟。

Description

There is going to be a voting at FIPA (Fédération Internationale de Programmation Association) to determine the host of the next IPWC (International Programming World Cup). Benjamin Bennett, the delegation of Diamondland to FIPA, is trying to seek other delegation's support for a vote in favor of hosting IWPC in Diamondland. Ben is trying to buy the votes by diamond gifts. He has figured out the voting price of each and every country. However, he knows that there is no need to diamond-bribe every country, since there are small poor countries that take vote orders from their respected superpowers. So, if you bribe a country, you have gained the vote of any other country under its domination (both directly and via other countries domination). For example, if C is under domination of B, and B is under domination of A, one may get the vote of all three countries just by bribing A. Note that no country is under domination of more than one country, and the domination relationship makes no cycle. You are to help him, against a big diamond, by writing a program to find out the minimum number of diamonds needed such that at least m countries vote in favor of Diamondland. Since Diamondland is a candidate, it stands out of the voting process.

Input

The input consists of multiple test cases. Each test case starts with a line containing two integers n (1 ≤ n ≤ 200) and m (0 ≤ m ≤ n) which are the number of countries participating in the voting process, and the number of votes Diamondland needs. The next n lines, each describing one country, are of the following form:

CountryName DiamondCount DCName1 DCName1 ...

CountryName, the name of the country, is a string of at least one and at most 100 letters and DiamondCount is a positive integer which is the number of diamonds needed to get the vote of that country and all of the countries that their names come in the list DCName1 DCName1 ... which means they are under direct domination of that country. Note that it is possible that some countries do not have any other country under domination. The end of the input is marked by a single line containing a single # character.

Output

For each test case, write a single line containing a number showing the minimum number of diamonds needed to gain the vote of at least m countries.

Sample Input

3 2Aland 10Boland 20 AlandColand 15#

Sample Output

20

题目:

给定由若干个树组成的森林, 树上的边是有向边, 树上的每个节点都有一个代价. 若要得到某个节点, 需要付出该节点对应的代价, 若该节点拥有后继, 那么后继的节点也都能获得. 求解使用最少的代价取得至少 m 个节点

思路:

1. 树形DP, 在森林里的每个树上分配配额. 

2. dp[i][j] 表示在第 i 个节点上获得 j 个节点所需要的最少代价. 那么 dp[i][j] = min(dp[i][j], dp[v][k]+dp[i][j-k])

3. 根据上面的状态转移方程式, 依然是个典型的树形 DP, 依然是 3 重循环, dfs(u) 即可, 甚至不用 pre, 因为给定的树边都是有向边

总结:

1. sscanf 用法

while( gets(str) && str[0] != '#') {  sscanf(str, "%d%d", &n,&m);

2. stringstream 用法, 需要先引入 sstream

gets(str);stringstream ss(str);while(ss >> name)

细节:

1. 输入比较坑爹. '#'加的完全是画蛇添足, 在处理这样的输入数据时, 用 sscanf是极为适合的, 因其有类似回滚的能力

2. gets(str) 函数可以从一行中的某一个位置处重新开始读取, 而不是必定读取一整行

3. 题目给出一个 m, 但是我们仍需要求解 dp[0][n]. 因为可能会出现这种情况, 直接贿赂父节点比贿赂其后继节点划算而使得 dp[0][i] > dp[0][j], j>i). dp[0][i]记录的是恰好得到 i 个节点的代价. 这个地方WA过

4. 输入给出的是几棵树, 但代码将其组成了一个森林, 这样的直接一次 dfs(0) 即可得到结果. 第一次看别人代码时没看懂. 这个技巧是有普适性的, 以后做树形 DP 时也要考虑能否使用

5. INF 的设置, 这道题没有像 communication system 那样, 将 dp 初始化为 -1, 而是简单的将其初始化为 INF, 以便于直接使用 min, 减少了很多判断(当然就会填满二维矩阵). 但是要注意, INF 不能太大, 要确保 INF+INF 不会溢出

6. POJ 似乎不支持 unordered_map, 第一次提交时 compile error 了

7. cost[0] 需要初始化为 INF

8. 下面代码中变量 j 的遍历顺序是从 n->1. 这就防止了同一个子树下的节点被取两次的问题. 同时 i 的取值是 u 的各个孩子节点, 抽象来看, 就是完全背包问题

代码

#include 
#include
#include
#include
#include
using namespace std;const int MAXN = 250;const int INF = 1e9;map
s2i;vector
graph[MAXN];int cost[MAXN];bool root[MAXN];int dp[MAXN][MAXN];int n, m;string name;int diamond;int dfs(int u) { // 有向图, 不需要考虑 visited or pre for(int i = 1; i <= n; i ++) dp[u][i] = INF; dp[u][0] = 0; int child = 1; for(int i = 0; i < graph[u].size(); i++) { int v = graph[u][i]; child += dfs(v); for(int j = n; j >= 0; j --) { for(int k = 0; k <= j; k ++) { dp[u][j] = min(dp[u][j], dp[u][j-k]+dp[v][k]); // INF+INF? } } } dp[u][child] = min(dp[u][child], cost[u]); return child;}int main() { freopen("E:\\Copy\\ACM\\poj\\3345_v2\\in.txt","r", stdin); char str[1000]; while( gets(str) && str[0] != '#') { sscanf(str, "%d%d", &n,&m); int id = 1; s2i.clear(); memset(root, 1, sizeof(root)); for(int i = 0; i <= n; i ++) graph[i].clear(); for(int i = 0; i < n; i ++) { //cin >>name>>diamond; scanf("%s %d", &str, &diamond); if(s2i.find(str) == s2i.end()) s2i[str] = id++; int u = s2i.find(str)->second; cost[u] = diamond; gets(str); stringstream ss(str); while(ss >> name) { if(s2i.find(name) == s2i.end()) s2i[name] = id++; int v = s2i.find(name)->second; root[v] = false; graph[u].push_back(v); } } for(int i = 1; i <= n; i ++) if(root[i]) graph[0].push_back(i); cost[0] = INF; dfs(0); int ans = INF; for(int i = m; i <= n; i ++) ans = min(ans, dp[0][i]); cout << ans << endl; // main function } return 0;}

  

 

转载地址:http://woyql.baihongyu.com/

你可能感兴趣的文章
.NET平台开源项目速览(9)软件序列号生成组件SoftwareProtector介绍与使用
查看>>
干货:史上最实用逃顶绝招十二式!
查看>>
鸟哥Linux私房菜 基础学习篇读书笔记(10):Linux磁盘和文件系统管理(3)
查看>>
简述Session 、Cookie、cache 区别
查看>>
large-scale analysis of malware downloaders
查看>>
pyqt声音输入
查看>>
FMX 模态窗体
查看>>
使用storyboard实现页面跳转,简单的数据传递
查看>>
有些事明显对自己有益,为什么却无法去做?
查看>>
IOS开发基础知识--碎片30
查看>>
C语言编程规范—命名规则
查看>>
批处理-剪切文件夹到指定目录
查看>>
java POi excel 写入大批量数据
查看>>
关于子类对象的构造函数和父类构造函数的执行顺序
查看>>
.NET Core Web 应用部署到 Docker 中运行
查看>>
Saltstack-API(十二)
查看>>
Asp.net Boilerplate源码中NotNullAttribute的用处
查看>>
javascript继承
查看>>
待处理
查看>>
linux下在root用户登陆状态下,以指定用户运行脚本程序实现方式
查看>>