题目链接: 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
<
=
j
<
=
i
<
=
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<=j<=i<=allLeafSon}leafson[i]*leafson[j]
∑i=1cnt−leafleafson[i]∗∑j=1i−1leafson[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;
}