hdu - 4340 - Capturing a country - 树形dp

http://acm.hdu.edu.cn/showproblem.php?pid=4340

题意:

题意:两个人进攻n个城市的国家,这n个城市构成一棵树,可以任意选择一个点开始,攻击已被自己攻击过的点的相邻点,时间可以减半,两个人的攻击时间不一
这个状态要求这个连通块有一个入口嘛


显然题中给图的是一颗树。问题可以抽象成对树的每个点都染色,有两中颜色可以选择。
我们可以知道,如果某一个连通的点集染的是同一种颜色,则这个集合中只要而且必须有一个点取完整的费用,其他的点都只需要对应费用的一半。
状态:dp[i][j][k] (0 <= i <= n, 0<=j<=1, 0 <= k <= 1) 表示以i为根的子树的费用,其中i节点被染成了第j种颜色,且子树中与i染成同一种颜色的与i连通的点集有k个点选取了完整的费用。
若选取1为根节点,则最后需要的结果为: min(dp[1][0][1], dp[1][1][1]}。
状态转移方程:
v为i节点的儿子节点。令 S = sum{min(dp[v][j][0], dp[v][1-j][1])}, det = min{dp[v][j][1] - min(dp[v][j][0], dp[v][1-j][1])};
dp[i][j][0] = cost[i][j]/2 + S;
dp[i][j][1] = min(cost[i][j] + S, cost[i][j]/2 + S + det);

#include<cstdio>
 #include<cstring>
 #include<iostream>
 #include<algorithm>
 #include<set>
 #include<map>
 #include<queue>
 #include<vector>
 #include<string>
 #define Min(a,b) a<b?a:b
 #define Max(a,b) a>b?a:b
 #define CL(a,num) memset(a,num,sizeof(a));
 #define maxn  205
 #define inf 9999999
 #define mx 1<<60
 using namespace std;
 vector<__int64>g[maxn];
 int dp[maxn][2][2],a[2][maxn],vis[maxn];
 void dfs(int k)
 {
     int i,j;
     int  len = g[k].size();
     if( len == 0)//  为叶子节点
     {
         dp[k][0][0] = a[0][k]/2;
         dp[k][0][1] = a[0][k];
         dp[k][1][0] = a[1][k]/2;
         dp[k][1][1] = a[1][k];
         return ;
     }

     for( i = 0;i < 2; ++i)
     {
         int sum = 0,det = inf ,tmp;
         for( j = 0; j < len ;++j)
         {
             dfs(g[k][j]);
             int w = g[k][j];
             tmp = min(dp[w][i][0],dp[w][1 - i][1]);
             sum +=tmp;
             det = min(det,dp[w][i][1] - tmp);
         }
         dp[k][i][0]  = sum + a[i][k]/2;
         dp[k][i][1]  = min(a[i][k] + sum ,a[i][k]/2 + sum + det);
     }
 }
 int main()
 {
     int n, i, j,x,y,root;
     while(scanf("%d",&n)!=EOF)
     {
         for(j = 0; j < 2 ;++j)
         {
             for( i = 1; i <= n ;++i )
             scanf("%d",&a[j][i]);
         }
         CL(vis,0);
         for(i = 0;i <= n;i++)g[i].clear();
         for(i = 0; i < n - 1 ;++i)
         {
           scanf("%d%d",&x,&y);
           if( i ==0) root = x;//建的图是有向图,对解决问题没有影响,树形dp 就是 这样
           if(!vis[y])
           {
               g[x].push_back(y);
               vis[x] = 1;
           }
           else g[y].push_back(x);

         }
         CL(dp,0);
         dfs(root);
         int t1 = dp[root][0][1];
         int t2 = dp[root][1][1];
         printf("%d\n",min(t1,t2));
     }
 }


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值