codeforces round#322 div2 F (树形背包)

题目意思:

给定一颗n(n<=5000)的树,求把多有节点分配给A,B且让叶子节点一半分给A,一半分给B 时不同分配相邻数的最小值。

分析:

直接树形背包,找一个不是叶子的节点当根节点。

d[ i ] [ j  ][ k ] 代表当前在i节点,i节点分配给了k,要完成i所在子树内叶子节点分给A j个所产生的最小相邻边数。

树形背包要注意的时在背包时,先前状态总是当前只考虑前面几个子树时所产生的最小值

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<string>
#include<cmath>
#include<cctype>
#include<ctime>
#include<bitset>
using namespace std;
#define rep1(i,x,y) for(int i=x;i<=y;i++)
const int N = 5e3 + 10;
const int oo = 0x3f3f3f3f;
int val[N<<1],cnt_,head[N],nex[N<<1],deg[N]={0};
void init(){
  cnt_ = 0;
  memset(head,-1,sizeof(head));
  memset(deg,0,sizeof(deg));
}
void addedge(int u,int v){
   val[++cnt_] = v;
   nex[cnt_] = head[u];
   head[u] = cnt_;
}
int n,d[N][N][2],siz[N],tem[N][2];
void dfs(int u,int fa){
   int cc = 0;
   d[u][0][1]=0; //当前u分配给1,总共叶子树分配零个,的最优质
   d[u][0][0]=0;
   siz[u] = 0;
   for(int p = head[u] ; p!=-1; p=nex[p]) if(val[p] !=fa){
      int v = val[p];
      dfs(v , u); cc++;
      for(int j = 0; j<= siz[u] ; j++ )
          for(int k=0; k < 2; k++) tem[j][k] = d[u][j][k]; //备份数组
      for(int i=0;i<=siz[v];i++)
         for(int j=0;j<=siz[u];j++){
           if(i == 0){//当i等于0时,每个状态最少想这个子树分配零,所以求取最小值,而不需保留前面的最优值。
             d[u][i+j][0] = min(d[v][i][1]+1+tem[j][0],d[v][i][0]+tem[j][0]);
             d[u][i+j][1] = min(d[v][i][0]+1+tem[j][1],d[v][i][1]+tem[j][1]);
           }
           else {
             d[u][i+j][0] = min(d[u][i+j][0],min(d[v][i][1]+1+tem[j][0],d[v][i][0]+tem[j][0]));
             d[u][i+j][1] = min(d[u][i+j][1],min(d[v][i][0]+1+tem[j][1],d[v][i][1]+tem[j][1]));
           }
      }
      siz[u]+=siz[v];
   }
   if(cc == 0){
      siz[u] = 1;
      d[u][1][0] = 0;
      d[u][0][0] = oo;
   }
}
int main()
{
   init();
   scanf("%d",&n);
   int x,y;
   rep1(i,1,n-1) scanf("%d %d",&x,&y),addedge(x,y),addedge(y,x),deg[x]++,deg[y]++;
   int root = -1;
   if(n == 2) {
       printf("1\n"); return 0;
   }
   int all = 0;
   rep1(i,1,n){
      if(deg[i] != 1) root = i;
      else all++;
   }
   memset(d,oo,sizeof(d));
   dfs(root,-1);
   cout<<min(d[root][all/2][0],d[root][all/2][1])<<endl;
   return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值