题目大意:
给出一棵n个点的树,求出最少添加几条边可以使得这颗树上的每个节点都在且仅在一个环内…..(题目比较坑人,没有说清楚这个环至少是三个节点)
如果无法满足,输出-1
分析:
想了很久还是没有思路……..
去看了一下状态是什么…..
然后自己试着推了推DP方程……ms是对的….
f[i][0]代表以i为根的子树每个顶点都在且仅在一个环中的ans
f[i][1]代表以i为根的子树除去根节点外每个顶点都在且仅在一个环中的ans
f[i][2]代表以i为根的子树除去根节点以及与根相连的一条链之外(也就是加上根节点至少有2个vertices)的ans
然后考虑每个根节点的状态可以由子节点的哪些状态转移而来…..
f[root][1]=∑f[to[i]][0]这是最显然的一个
f[root][2]=f[to[i]][0]*(k-1)+min(f[to[i]][1],f[to[i]][2])
就是说在root的k个子节点中,有k-1个都已经变成了满足题目要求的图,还有一颗子树有一个顶点或者一条链没有在环中,那么这条链或者这个顶点就与root组成了新的一条链
f[root][0]=min(A,B)
A=f[to[i]][0]*(k-1)+f[to[i]][2]+1
如图:(画的不太好(????))
B=f[to[i]][0]*(k-2)+min(f[to[i]][2],f[to[i]][1])*2+1
大概就是有两条链,然后往中间添一条边……
代码如下:
//by NeighThorn #include#include #include #include #define inf 0x3f3f3f3f using namespace std; const int maxn=100+5; int n,hd[maxn],to[maxn*2],nxt[maxn*2],cnt,f[maxn][3]; inline int read(void){ char ch=getchar();int f=1,x=0; while(!(ch>='0'&&ch<='9')){ if(ch=='-')f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return f*x; } inline void add(int x,int y){ to[cnt]=y; nxt[cnt]=hd[x]; hd[x]=cnt++; } //f[i][0]代表以i为根的子树每个顶点都在且仅在一个环中的ans //f[i][1]代表以i为根的子树除去根节点外每个顶点都在且仅在一个环中的ans //f[i][2]代表以i为根的子树除去根节点以及与根相连的一条链之外(也就是加上根节点至少有2个vertices)的ans //f[i][1]=∑(k)f[to[i]][0] //f[i][2]=∑(k-1)f[to[i]][0] +(1)min(f[to[i]][1],f[to[i]][2]) //f[i][0]=∑(k-2)f[to[i]][0] +(2)min(f[to[i]][1],f[to[i]][2])+1 //f[i][0]=∑(k-1)f[to[i]][0] +(1)f[to[i]][2]+1 inline void dfs(int root,int fa){ int sum=0,min1=inf,min2=inf,minver2=inf; for(int i=hd[root];i!=-1;i=nxt[i]) if(to[i]!=fa){ dfs(to[i],root); sum+=f[to[i]][0]; minver2=min(minver2,f[to[i]][2]-f[to[i]][0]); if(min(f[to[i]][2]-f[to[i]][0],f[to[i]][1]-f[to[i]][0]) =inf?-1:f[1][0])<