poj 1947(树形背包问题)

来源:互联网 发布:ubuntu配置php环境 编辑:程序博客网 时间:2024/05/04 03:22

题意:给定一棵树,求获取一棵节点数为p的子树需要切割的边的最小数量

做法:定义数组dp[i][j]为以i为根节点获取节点数为j的树需要切割的最小的边的数量。对于i的一个子节点v,如果不要以v为根节点的整棵子树,那么dp[i][j]=dp[i][j]+1(因为要切割i与v的相连的边)。否则,可以枚举以v为根节点的子树的大小,假设为W,那么dp[i][j]=min(dp[i][j].dp[i][j-w]+dp[v][w])。

一些小细节:如果是开二维数组来做,像上面一样的,那么对于当前的根节点i,在枚举第二维的时候需要逆序枚举(就像01背包的做法)。还有,dp[i][1]一开始要初始化为0,dp[i][sum[i]]=0(sum[i]是i节点为根节点的整棵子树的大小。


#define _CRT_SECURE_NO_WARNINGS#include<algorithm>#include<iostream>#include<ctime>#include<cmath>#include<cstring>#include<cstdio>#include<climits>using namespace std;const int maxn = 155;const int inf = INT_MAX - 5;int fir[maxn], vv[maxn], nxt[maxn], e;int sonnum[maxn], dp[maxn][maxn], desc[maxn];void add(int a, int b){vv[e] = b;nxt[e] = fir[a];fir[a] = e++;}void dfs(int cur){desc[cur] = 1;for (int i = fir[cur]; i != -1; i = nxt[i]){int v = vv[i]; dfs(v);desc[cur] += desc[v];}}void dfs1(int cur){dp[cur][1] = 0;dp[cur][desc[cur]] = 0;int sonind = 0;for (int i = fir[cur]; i != -1; i = nxt[i]){sonind++;int v = vv[i]; dfs1(v);for (int j = desc[cur]; j >=1; j--){if (dp[cur][j] != inf) dp[cur][j] = dp[cur][j] + 1;//cut the edge to this sonfor (int k = 1; k <= desc[v]; k++){if (k >= j) break;if (dp[cur][j - k]!=inf && dp[v][k]!=inf)dp[cur][j] = min(dp[cur][j], dp[cur][j - k] + dp[v][k]);}}}}int main(){int n, p;while (scanf("%d%d", &n, &p) != EOF){e = 0;for (int i = 1; i <= n; i++){fir[i] = -1;}for (int i = 1; i <= n; i++){for (int k = 0; k <= n; k++){dp[i][k] = inf;}}memset(sonnum, 0, sizeof(sonnum));memset(desc, 0, sizeof(desc));int a, b;for (int i = 1; i <= n - 1; i++){scanf("%d%d", &a, &b);add(a, b);sonnum[a]++;}dfs(1);dfs1(1);int ans = INT_MAX;ans = min(dp[1][p],ans);for (int i = 2; i <= n; i++){ans = min(ans, dp[i][p] + 1);}printf("%d\n", ans);}}

0 0
原创粉丝点击