传送门:http://codeforces.com/problemset/problem/581/F
思路:设f[i][cnt][col]表示i的子树中,黑节点为cnt,i的颜色为col时,最小的端点颜色不同的边的数量。
转移就是 f[x][cnt][col]=min(f[son[x]][i][col_son]+f[x][cnt-i][col]+(col!=col_son));
看起来这是O(n^3)的,实际上这是O(n^2)的
对于一个点x,对x操作的复杂度就是
观察一下就会发现,每对叶子节点(x,y)只会在lca(x,y)处对复杂度有一次贡献
于是复杂度就是O(n^2)的了。
#include<cstdio>
#include<cstring>
#include<algorithm>
const int maxn=5010,maxm=10010;
using namespace std;
int n,m,pre[maxm],now[maxn],son[maxm],val[maxm],f[maxn][maxn][2],g[maxn][2],siz[maxn],tot,in[maxn];bool isl[maxn];
void add(int a,int b){pre[++tot]=now[a],now[a]=tot,son[tot]=b,in[b]++;}//0 white 1 black
void dfs(int x,int fa){
if (isl[x]){siz[x]=1,f[x][0][0]=f[x][1][1]=0;return;}
f[x][0][0]=f[x][0][1]=0;
for (int y=now[x];y;y=pre[y])if (son[y]!=fa){
dfs(son[y],x);
//printf("fuckpp%d %d\n",son[y],siz[x]);
memset(g,63,sizeof(g));
for (int i=siz[x];i>=0;i--)
for (int j=siz[son[y]];j>=0;j--){
//if (son[y]==8&&x==5) printf("%d %d\n",i,j);
g[i+j][0]=min(g[i+j][0],f[x][i][0]+f[son[y]][j][0]);
g[i+j][1]=min(g[i+j][1],f[x][i][1]+f[son[y]][j][1]);
g[i+j][0]=min(g[i+j][0],f[x][i][0]+f[son[y]][j][1]+1);
g[i+j][1]=min(g[i+j][1],f[x][i][1]+f[son[y]][j][0]+1);
}
memcpy(f[x],g,sizeof(f[x]));
siz[x]+=siz[son[y]];
}
}
int main(){
scanf("%d",&n);
for (int i=1,a,b;i<n;i++) scanf("%d%d",&a,&b),add(a,b),add(b,a);
if (n==2) return puts("1"),0;
for (int i=1;i<=n;i++) isl[i]=(in[i]==1);
memset(f,63,sizeof(f));
for (int i=1;i<=n;i++) if (in[i]!=1){
dfs(i,0),printf("%d\n",min(f[i][siz[i]>>1][0],f[i][siz[i]>>1][1]));
break;
}
// for (int i=1;i<=n;i++) printf("%d %d\n",i,siz[i]);
return 0;
}