POJ 1848

题意:给定一棵树,要添加一些边,不能是重边,不能是自环,让它的每一个结点都恰好属于一个环,求最少添加的边数。

题解:树形dp:dp[now][0]为以now为根的子树完成题目要求添加的最少边。

        dp[now][1]为除开now这个结点后它的子树满足题目要求所需要的最少边。

        dp[now][2]为now和它的某一个孩子形成至少长度为2的链所需要添加的最少边,未涉及到的点均已满足题目要求。

   转移方程:dp[now][1]=sum(dp[k][0]),k为now的孩子。

        dp[now][2]=sum(dp[k][0])-dp[i][0]+min(dp[i][1],dp[i][2]),即从孩子中找一个,now会与它组成长度大于等于2的链

        dp[now][0]=sum(dp[k][0])-dp[i][0]+dp[i][2]+1或sum(dp[k][0])-dp[i][0]-dp[j][0]+min(dp[i][1],dp[i][2])+min(dp[j][1],dp[j][2]),前者是找一个有长度至少为2的链的孩子,后者是找两个没有形成环的孩子。

View Code
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int inf=1000,N=105;
 6 int dp[N][3],head[N],nc;
 7 bool vis[N];
 8 struct Edge
 9 {
10     int to,next;
11 } edge[N*4];
12 void add(int a,int b)
13 {
14     edge[nc].to=b;
15     edge[nc].next=head[a];
16     head[a]=nc++;
17     edge[nc].to=a;
18     edge[nc].next=head[b];
19     head[b]=nc++;
20 }
21 void dfs(int now)
22 {
23     vis[now]=true;
24     int t,stk[N],top=0,sumc=0,v;
25     dp[now][1]=0;
26     for(int i=head[now]; i!=-1; i=edge[i].next)
27     {
28         t=edge[i].to;
29         if(vis[t])
30             continue;
31         stk[top++]=t;
32         dfs(t);
33         sumc+=dp[t][0];
34     }
35     if(top==0)
36     {
37         dp[now][0]=dp[now][2]=inf;
38         dp[now][1]=0;
39         return;
40     }
41     dp[now][1]=sumc;
42     v=inf;
43     for(int i=0;i<top;i++)
44     {
45         t=stk[i];
46         v=min(v,sumc-dp[t][0]+min(dp[t][1],dp[t][2]));
47     }
48     dp[now][2]=v;
49     v=inf;
50     for(int i=0;i<top;i++)
51     {
52         int t1=stk[i];
53         v=min(sumc-dp[t1][0]+dp[t1][2]+1,v);
54         for(int j=i+1;j<top;j++)
55         {
56             int t2=stk[j];
57             v=min(sumc-dp[t1][0]-dp[t2][0]+min(dp[t2][1],dp[t2][2])+min(dp[t1][1],dp[t1][2])+1,v);
58         }
59     }
60     dp[now][0]=v;
61 }
62 int main()
63 {
64     int n;
65     while(scanf("%d",&n)!=EOF)
66     {
67         memset(head,-1,sizeof(head));
68         nc=0;
69         memset(vis,false,sizeof(vis));
70         for(int i=1,a,b;i<n;i++)
71         {
72             scanf("%d%d",&a,&b);
73             add(a,b);
74         }
75         dfs(1);
76         if(dp[1][0]<inf)
77             printf("%d\n",dp[1][0]);
78         else
79             printf("-1\n");
80     }
81     return 0;
82 }

转载于:https://www.cnblogs.com/tmeteorj/archive/2012/09/27/2706127.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值