http://codeforces.com/problemset/problem/212/E
结论: 两个颜色之和最大值一定是N-1 (根节点不染色,子树染成红或蓝)
DP[i][j] 表示 i 节点不染色,j 个点染成红色是否可行 (蓝色节点个数可以计算出来)
剩下的就是背包了,不要忘记对每个点的父亲也进行背包(因为是假设该点为根不染色)
最后统计答案的时候去掉只有一种颜色的情况
代码:
#include<cstdio> #include<iostream> #include<cstring> #include<cmath> #include<algorithm> using namespace std; typedef long long ll; const int maxn = 5010; int h[maxn],cnt=0; int n,ans = 0; int dp[maxn][maxn],is_red[maxn]; // i 节点不染色,有 j 个红色点是否可行 struct E{ int to,next; }e[maxn*2]; void add(int u,int v){ e[++cnt].next = h[u]; e[cnt].to = v; h[u] = cnt; } int sz[maxn]; void dfs(int u,int par){ sz[u] = 1; dp[u][0] = 1; for(int i=h[u];i!=-1;i=e[i].next){ int v = e[i].to; if(v==par) continue; dfs(v,u); sz[u] += sz[v]; } for(int i=h[u];i!=-1;i=e[i].next){ int v=e[i].to; if(v==par) continue; for(int j=n-1;j>=sz[v];--j){ // 0/1 背包 dp[u][j] |= dp[u][j-sz[v]]; // 注意一定要或!! // printf("%d %d %d %d ",u,v,j,dp[u][j]); } } int s = n - sz[u]; for(int j=n-1;j>=s;--j){ dp[u][j] |= dp[u][j-s]; // printf("%d %d %d %d ",u,par,j,dp[u][j]); } for(int i=1;i<n-1;++i){ if(dp[u][i] && !is_red[i]){ ++ans; is_red[i] = 1; } } } ll read(){ int f=1,s=0; char ch = getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; else ch = getchar(); } while(ch>='0' && ch<='9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; } int main(){ memset(h,-1,sizeof(h)); n = read(); int u,v; for(int i=1;i<n;i++){ u = read(),v = read(); add(u,v),add(v,u); } dfs(1,0); printf("%d ",ans); for(int i=1;i<n-1;i++){ if(is_red[i]){ printf("%d %d ",i,n-1-i); } } return 0; }