题意:给定一棵树,要添加一些边,不能是重边,不能是自环,让它的每一个结点都恰好属于一个环,求最少添加的边数。
题解:树形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的链的孩子,后者是找两个没有形成环的孩子。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }