codeforces 212E 树上背包

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\n",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\n",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\n",ans);
    
    for(int i=1;i<n-1;i++){
        if(is_red[i]){
            printf("%d %d\n",i,n-1-i);
        }
    }

    return 0;
}

 

posted @ 2020-10-03 16:51  Tartarus_li  阅读(165)  评论(0编辑  收藏  举报