POJ 1947 Rebuilding Roads

大概的题意是给出n,p,一共有n个节点,问你要剩下p个节点,最少减去最少的边是多少。
dp[root][i]:记录root结点,要得到一棵i个节点的子树去掉的最少边数

详细解释看代码:

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 using namespace std;
 5 #define N 160
 6 #define INF 0x3ffffff
 7 
 8 int dp[N][N],son[N],bro[N],fa[N];
 9 int root,n,p;
10 
11 void dfs(int root)
12 {
13     int tem;
14     for(int i=0; i<=p; i++)
15         dp[root][i] = INF;
16 
17     dp[root][1] = 0;//在root处想得到[i]个节点的子树所要去掉的最少边数
18     int k = son[root];//考虑其儿子k
19 /*
20 切去k子树:tem = dp[root][i] + 1
21 不切掉k子树:tem = min(dp[k][i-j]+dp[root][j],tem)
22 */
23     while(k)
24     {
25         dfs(k);
26         for(int i=p; i>=1; i--)//类似背包的处理
27         {
28             tem = dp[root][i] + 1;//在root处想得到i个节点子树时,进行剪掉子树
29 
30             for(int j=1; j<i; j++)
31                 tem = min(dp[k][i-j]+dp[root][j],tem);//不剪去第k个子树,然后的到所需节点数的边数dp[k][i-j]+dp[root][j]
32 
33             dp[root][i] = tem;
34         }
35         k = bro[k];
36     }
37 }
38 
39 int solve()
40 {
41     dfs(root);
42     int ans = dp[root][p];
43     for(int i=1; i<=n; i++)
44         ans = min(dp[i][p]+1,ans);
45     printf("%d\n",ans);
46     return ans;
47 }
48 int main()
49 {
50     //freopen("in.txt","r",stdin);
51     int a,b;
52     while(~scanf("%d%d",&n,&p) )
53     {
54         memset(fa,0,sizeof(fa));
55         memset(son,0,sizeof(son));
56         //memset(brother,0,sizeof(brother));
57 
58         for(int i=1; i<n; i++)
59         {
60             scanf("%d%d",&a,&b);//brother[i]记录i节点的儿子数,brother[i]记录i节点的兄弟数
61             fa[b] = 1;//标记有父亲不是根
62             bro[b] = son[a];//记录同一个父亲的上一个兄弟
63             son[a] = b;//该父亲现在的儿子
64         }
65         /*
66         for(int i=1; i<12; i++)
67         {
68             printf("~~fa[%d]=%d  ",i,fa[i]);
69             printf("~~bro[%d]=%d  ",i,bro[i]);
70             printf("~~son[%d]=%d\n",i,son[i]);
71         }
72         */
73         for(int i=1; i<=n; i++)//找到根节点
74         {
75             if(fa[i] == 0)
76             {
77                 root = i;
78                 break;
79             }
80         }
81 
82         solve();
83     }
84     return 0;
85 }

 

转载于:https://www.cnblogs.com/man1304010109/p/3658323.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值