比赛总结
题目
题意:
一棵有n个点的树,可以选择一些边,但是同一个点连出的边最多选一条,求最多可以选几条,且输出方案。
题解:
树形DP,每个点记录两个值:连向儿子的边中,选一条的最优值和一条都不选的最优值。
//Time:93ms //Memory:5925KB //Length:1648B #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <queue> using namespace std; #define MAXN 200010 int he[MAXN],to[MAXN],nex[MAXN],id[MAXN]; int a[MAXN],b[MAXN]; bool vi[MAXN]; int num[2][MAXN],top; void add(int u,int v,int i) { to[top]=v; nex[top]=he[u]; id[top]=i; he[u]=top++; } void cal(int h,int fa) { int sum=0,tmp=0; for(int i=he[h];i!=-1;i=nex[i]) if(fa!=to[i]) cal(to[i],h),sum+=num[1][to[i]]; num[0][h]=sum; for(int i=he[h];i!=-1;i=nex[i]) if(fa!=to[i]) tmp=max(tmp,sum-num[1][to[i]]+num[0][to[i]]+1); num[1][h]=tmp; } void check(int h,int fa,bool flag) { int sum=0; if(num[0][h]>num[1][h]) flag=1; for(int i=he[h];i!=-1;i=nex[i]) if(fa!=to[i]) sum+=num[1][to[i]]; for(int i=he[h];i!=-1;i=nex[i]) if(fa!=to[i]) { if(sum-num[1][to[i]]+num[0][to[i]]+1==num[1][h]) if(!flag) { vi[id[i]]=1,flag=1,check(to[i],h,1); continue; } check(to[i],h,0); } } int main() { //freopen("/home/moor/Code/input.txt","r",stdin); int n,m; scanf("%d%d",&n,&m); memset(num,0,sizeof(num)); memset(he,-1,sizeof(he)); memset(vi,0,sizeof(vi)); top=0; for(int i=0;i<m;++i) { scanf("%d%d",&a[i],&b[i]); add(a[i],b[i],i); add(b[i],a[i],i); } cal(1,-1); printf("%d\n",max(num[0][1],num[1][1])); check(1,-1,0); for(int i=0;i<m;++i) if(vi[i]) printf("%d %d\n",a[i],b[i]); return 0; }