您的位置:首页 > 产品设计 > UI/UE

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  acm