cf581F 依赖背包+临时数组 好题
这题得加个临时数组才能做。。
/* 给定一棵树,树节点可以染黑白,要求叶子节点黑白平分 称连接黑白点的边为杂边,求使得杂边最少的染色方 那么设dp[i][j][0|1]表示i子树中有j个叶子节点,i染黑或白 那么其实是依赖背包,即枚举每个节点的字数v,进行分组即可 给dp初始化0x3f 边际条件:如果i是叶子节点,那么dp[i][i][0|1]=0; */ #include<bits/stdc++.h> using namespace std; #define maxn 5005 struct Edge{int to,nxt;}edge[maxn<<1]; int n,k,flag[maxn],num[maxn],root,dp[maxn][maxn][2],tot,head[maxn]; void init(){ memset(head,-1,sizeof head); tot++; } void addedge(int u,int v){ edge[tot].to=v;edge[tot].nxt=head[u];head[u]=tot++; } int dfs1(int u,int pre){ num[u]=0; if(flag[u]==1)return num[u]=1; for(int i=head[u];i!=-1;i=edge[i].nxt){ int v=edge[i].to; if(v!=pre)dfs1(v,u),num[u]+=num[v]; } return num[u]; } void dfs2(int u,int pre){ if(flag[u]==1){ dp[u][1][0]=dp[u][0][1]=0; return; } for(int i=head[u];i!=-1;i=edge[i].nxt){ int v=edge[i].to; if(v!=pre)dfs2(v,u); } int tmp[maxn][2];//临时数组,tmp[j]表示j个黑点的最小杂边 dp[u][0][0]=dp[u][0][1]=0;//这两种情况 for(int i=head[u];i!=-1;i=edge[i].nxt){ int v=edge[i].to; if(v==pre)continue; memset(tmp,0x3f,sizeof tmp); for(int j=num[u];j>=0;j--) for(int t=num[v];t>=0;t--){ tmp[j][0]=min(tmp[j][0],dp[u][j-t][0]+min(dp[v][t][0],dp[v][t][1]+1)); tmp[j][1]=min(tmp[j][1],dp[u][j-t][1]+min(dp[v][t][1],dp[v][t][0]+1)); } for(int j=num[u];j>=0;j--)//每次更新完一次tmp数组都要更新到dp里 dp[u][j][0]=tmp[j][0],dp[u][j][1]=tmp[j][1]; } } int main(){ cin>>n; int u,v;init(); for(int i=1;i<n;i++){ cin>>u>>v; addedge(u,v);addedge(v,u); flag[u]++,flag[v]++; } if(n==2){ printf("%d\n",n-1); return 0; } memset(dp,0x3f,sizeof dp); root=1; while(flag[root]==1)root++; dfs1(root,0); k=num[root]/2; dfs2(root,0); printf("%d\n",min(dp[root][k][0],dp[root][k][1])); }