Codeforces Round #322 Zublicanes and Mumocrates 树形dp

题目链接: http://codeforces.com/contest/581/problem/F

题意:

给你一棵有偶数个叶子结点的树,现在要求给树上的每个结点染黑色或者白色,叶子结点必须有一半为黑色一半为白色,其余的结点的颜色可以任意染,当这一边两边的叶子结点颜色不同时,这条边对答案的贡献 + 1 +1 +1 , 问你答案最小是多少,即最少有多少对边满足边两端的颜色不同。

做法:

一棵树并且是问到了最小,那么树形 d p dp dp 应该还是可以想象的, d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k] 代表,到达结点 i i i 的时候 ,其子树中有 j j j 个黑色结点,并且自己染成了 k k k 色的最小代价。

那么在转移的过程中其实就是在自己和儿子之间做一个 01 01 01背包,将儿子所有可能的最优状态转移过来。

问题就是复杂度的计算了,在做题的时候算成了 O ( n 3 ) O(n^3) O(n3) 的所以没敢敲,但其实是 O ( n 2 ) O(n^2) O(n2)的。
对于一个结点 x x x ,它的最坏时间复杂度是 ∑ i = 1 c n t − l e a f l e a f s o n [ i ] ∗ ∑ j = 1 i − 1 l e a f s o n [ j ] = ∑ 1 &lt; = j &lt; = i &lt; = a l l L e a f S o n l e a f s o n [ i ] ∗ l e a f s o n [ j ] \sum_{i=1}^{cnt-leaf} leafson[i]*\sum_{j=1}^{i-1} leafson[j] = \sum_{1&lt;=j&lt;=i&lt;=allLeafSon}leafson[i]*leafson[j] i=1cntleafleafson[i]j=1i1leafson[j]=1<=j<=i<=allLeafSonleafson[i]leafson[j]

代码

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i = (int)a;i<=(int)b;i++)
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn=5005;
const int inf=0x3f3f3f3f;
vector<int> ve[maxn];
int dp[maxn][5005][2];
int rt,n,lef,sz[maxn];
void deal(int u,int v,int f){
    for(int i=sz[u];i>=0;i--){
        int tmp=inf;
        for(int j=0;j<=sz[v]&&j<=i;j++){
            tmp=min(tmp,dp[u][i-j][f]+dp[v][j][0]+(f!=0));
            tmp=min(tmp,dp[u][i-j][f]+dp[v][j][1]+(f!=1));
        }
        dp[u][i][f]=tmp;
    }
}
void dfs(int u,int f){
    for(int i=0;i<maxn;i++)
        dp[u][i][0]=dp[u][i][1]=inf;

    dp[u][0][0]=dp[u][0][1]=0;

    for(auto v:ve[u]){
        if(v==f) continue;
        dfs(v,u);
        sz[u]+=sz[v];
        deal(u,v,0);
        deal(u,v,1);
    }
    if(ve[u].size()==1){
        sz[u]=1;
        dp[u][0][1]=inf;
        dp[u][0][0]=0;
        dp[u][1][1]=0;
        lef++;
    }
}
int main(){
    scanf("%d",&n);
    rep(i,1,n-1){
        int x,y; scanf("%d%d",&x,&y);
        ve[x].push_back(y);
        ve[y].push_back(x);
    }
    if(n==2) return 0*printf("1\n");
    rep(i,1,n){
        if(ve[i].size()>1){
            rt=i;
            break;
        }
    }
    dfs(rt,-1);
    printf("%d\n",min(dp[rt][lef/2][0],dp[rt][lef/2][1]));
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值