uva 10859 放置街灯树形dp

首先,本题的优化目标有两个:放置的街灯a应该尽量少;被两灯同时照亮的边数b应该尽量大。为了统一起见,我们把后者替换为:恰好被一盏灯照亮的边数c应该尽量小,然后改用x = Ma+c作为优化目标,其中一个M为很大的正整数。当x取到最小值时,x/M的整数部分是放置街灯的最小值,x%M为恰好被一盏灯照亮的边数的最小值。

一般来说,如果有两个需要优化的目标v1和v2时,要求首先满足v1最小,在v1最小的情况的下v2最小,则可以将二者组合成一个量Mv1+v2,其中M是一个比“v2的最大理论值和v2的最小理论值之差”还要大的数,这样,只要两个解的v1不同,则v2不管相差多少,都是v11起到决定性的作用;只有当v1相同时,才取决于v2。本题中M取2000.

/********************
 * Author :fisty
 * data:2014-12-10
 * uva 10859
 * 树形DP
 * ********************/

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define MAX_N 1010
vector<int> G[MAX_N];
int vis[MAX_N][2], d[MAX_N][2], n, m;

int dp(int i, int j, int fa){
        //j是i的父节点状态(1 为有灯, 0 为无灯)
        //fa是i的父节点
        if(vis[i][j]) return d[i][j];
        vis[i][j] = 1;
        int& ans = d[i][j];   //ans是对dp进行引用
                        
        ans = 2000;             //灯的数量加1,x加2000
        //决策1: i结点放灯,d(i,j) = sum{d(k,1)|k取遍i的所有子节点} + 2000;
        for(int k = 0;k < G[i].size(); k++){
                if(G[i][k] != fa){
                        ans += dp(G[i][k], 1, i); //
                }       
        }               
        if(fa >= 0 && !j)   ans++;          //如果i不是根节点还要加上i与父节点的边
        //决策2:i结点不放灯(只有在i是根节点或者父结点放了灯才可以执行)
        //d(i,j) = sum{d(k, i)|k取遍所有i的所有子结点}+2000
        if(j != 0 || fa == -1){
                //i是根或者i 父节点放了灯
                int sum = 0;
                for(int k = 0;k < G[i].size(); k++){
                        if(G[i][k] != fa){
                                sum += dp(G[i][k], 0, i);
                        }
                }
                if(fa >= 0) sum++;              //i不是根
                ans = min(ans , sum);           //在两个决策中选一个最小值
         }
        return ans;
}
int main(){
        int t, a, b;
        scanf("%d", &t);
        while(t--){
              scanf("%d%d", &n, &m);
              for(int i = 0;i < n; i++) G[i].clear();

              for(int i = 0;i < m; i++){
                        scanf("%d%d", &a, &b);
                        G[a].push_back(b);
                        G[b].push_back(a);
              }
              memset(vis, 0, sizeof(vis));
              int ans = 0;
              for(int i = 0;i < n; i++){
                        if(!vis[i][0]){
                                ans += dp(i, 0, -1);    //i是根没有父节点,为-1;
                        }
              }
              printf("%d %d %d\n", ans / 2000, m - ans % 2000, ans % 2000);
        }
        return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值