POJ 1947 Rebuilding Roads (树形dp + 01背包)
2016-07-22 21:09
363 查看
题目链接:
http://poj.org/problem?id=1947题解:
开一个dp数组,dp[i][j]表示以第i个节点为根,保留j个点所需要的刀数。可以先将所有dp[i][1]初始化为 i的儿子数(仅保留这个点需要的刀数),其他位置初始化为无限大。然后将问题看做一个01背包,物品的容量就是m,此时和普通的01背包区别为背包里的物品(也就是子树)可以选择k个节点砍掉或者不砍(j + k <= m)。所以d[i][j] = d[i][j-k]+d[son[i]][k]-1;(两子树拆开需要一刀)。AC代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; int dp[200][200], father[200]; vector <int> fson[200]; int n,m; void init() { for(int i = 1; i <= n; i++) { for(int j = 1; j <= m; j++) dp[i][j] = 1000000; } } int dfs(int root) { for(int i = 0; i < fson[root].size(); i++) dfs(fson[root][i]); dp[root][1] = fson[root].size();//只要一个点时有多少个儿子砍多少刀 for(int i = 0; i < fson[root].size(); i++) for(int j = m - 1; j > 0; j--)//这里j需要从大往小和01背包类似, m为背包容量,dp记载花费,k代表我要拿的物品子树内的点,j代表我要拿的物品子树外的点 if(dp[root][j] != 1000000) for(int k = 1; k <= m - j; k++) if(dp[fson[root][i]][k] != 1000000) dp[root][j + k] = min(dp[root][j] + dp[fson[root][i]][k]-1,dp[root][j + k]);//我砍了物品子树内的点和物品子树外的点所需要的最小花费 return dp[root][m]; } int main() { while(cin >> n >> m) { init(); int n1,n2,cnt = 0; for(int i= 0; i < n - 1; i++) { cin >> n1 >> n2; father[n2] = n1; fson[n1].push_back(n2); } int root; for(int i = 1; i <= n; i++) if(!father[i]) root = i; int minn = dfs(root); for(int i = 1; i <= n; i++) { if(i != root && minn > dp[i][m]) minn = dp[i][m] + 1; //之所以加1是需要将以i为根的子树与整棵树分离 } cout << minn <<endl; memset(father,0,sizeof(father)); memset(dp,0,sizeof(dp)); for(int i = 0;i <= n; i++) fson[i].clear(); } return 0; }
相关文章推荐
- 简单的四则运算
- 数的奇偶性
- ACMer博客瀑布流分析
- ACM程序设计大赛题目分类
- 2015年acm国内排名
- 计算字符串最后一个单词长度
- ACM网址
- 1272 小希的迷宫
- 1272 小希的迷宫
- hdu 1250 大数相加并用数组储存
- 矩阵的乘法操作
- 蚂蚁爬行问题
- 蚂蚁爬行问题
- 求两个数的最大公约数【ACM基础题】
- 打印出二进制中所有1的位置
- 杭电题目---一只小蜜蜂
- HDOJ 1002 A + B Problem II (Big Numbers Addition)
- 初学ACM - 半数集(Half Set)问题 NOJ 1010 / FOJ 1207
- 初学ACM - 组合数学基础题目PKU 1833
- POJ ACM 1002