Tree2cycle
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4714
题意:
给出一棵树,可以删边和加边将这棵树变成一个圈(没有任何分支),求最少的操作次数。
题解:
将树改造为圈可以通过现将树变成一条没有分叉的链后再加上一条边连接链的两端。
再简化一下就变成了选一条路径作为主干,再将其他所有分叉通过增删边连接到竹竿上的过程。
设DP1[i](把以 i 为根节点的子树作为主干的一部分),和DP2[i](把以 i 为根节点的子树作为分叉),再跑树形DP就行了。
代码
#include<stdio.h>
#include<queue>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
const int N=1e6+1;
int head[N],cnt,Ev[2*N],Enext[2*N];
int dp1[N]/*把以 i 为根节点的子树作为主枝*/,dp2[N]/*分叉*/;
int mmin(int x,int y)
{
return x<y?x:y;
}
void clean(int n)
{
cnt=0;
for(int i=1;i<=n;++i)
head[i]=-1;
}
void addedge(int u,int v)
{
Ev[cnt]=v;
Enext[cnt]=head[u];
head[u]=cnt++;
}
void TreeDp(int id,int fa)
{
dp1[id]=dp2[id]=0;
int sum1=0,sum2=0,m1=0,m2=0,son=0;
for(int i=head[id];i!=-1;i=Enext[i])
if(Ev[i]!=fa)
{
int v=Ev[i];
TreeDp(v,id);
sum1+=dp1[v];
sum2+=dp2[v];
if(!son)m1=dp1[v]-dp2[v];
else
{
if(dp1[v]-dp2[v]<=m1)
{
m2=m1;
m1=dp1[v]-dp2[v];
}
else if(dp1[v]-dp2[v]<m2)m2=dp1[v]-dp2[v];
}
son++;
}
if(son)
{
if(id==1)
{
int ans=mmin(sum2+m1+1,sum2+m1+m2+1);
printf("%d
",ans);
return ;
}
dp1[id]=sum2+m1;
dp2[id]=mmin(sum2+2,dp1[id]+2);
if(son>1)dp2[id]=mmin(dp2[id],sum2+m1+m2+2);
}
else
{
dp1[id]=0;
dp2[id]=2;
}
}
void solve()
{
int T,id,n,x,y,sum;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
clean(n);
sum=id=0;
for(int i=1;i<n;++i)
{
scanf("%d%d",&x,&y);
addedge(x,y);
addedge(y,x);
}
TreeDp(1,1);
}
}
int main()
{
solve();
return 0;
}