二分+树形dp

题意: 给出一棵树,树的叶子节点是前线,节点1是总指挥的地方。 现在想破环一些边让前线上的点都不能给1发送消息。现在想要使破坏边的最大值最小,同时还要让总的费用不超过m。单的个数为不超过1000, m不超过1000000.

每条边的值不超过1000.

 

思路: 这道题是很明显的树形dp。  由于要让最大的值最小,所以可以想到二分。 二分这个最大值。然后进行树形dp,看总的消耗费用是否大于m。 关于dp的方程很简单,这里就不写了。

AC代码:

 

View Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
const int N = 1005, INF = 1<<25;

struct EDGE
 {
     int u, v, w, next;
 }edge[N*2];

int dp[N], n, m, num, head[N];

void add(int u, int v, int w)
 {
     edge[num].u = u;
     edge[num].v = v;
     edge[num].w = w;
     edge[num].next = head[u];
     head[u] = num++;
 }

void init()
 {
     num = 0;
     memset(head, -1, sizeof(head));
     int u, v, w;
     for(int i=1; i<n; i++)
      {
          scanf("%d%d%d", &u, &v, &w);
          add(u, v, w);
          add(v, u, w);
      }
 }

void dfs(int u, int pre, int ww)
 {
     int v, tp = 0, w;
     bool flag = 0;

     for(int i=head[u]; i!=-1; i=edge[i].next)
      {
          v = edge[i].v;
          w = edge[i].w;
          if(v == pre) continue;
          flag = 1;
           dfs(v, u, ww);
          if(w > ww)
           tp += dp[v];
          else tp += min(w, dp[v]);
          if(tp >= INF)
           tp = INF;
      }
    if(flag)
     {
         dp[u] = tp;
     }
 }

void solve()
 {
     int ans = INF, left=0, right = 1005, mid;
     while(left <= right)
      {
          mid = (left+right)>>1;
          for(int i=1; i<=n; i++)
           {
               dp[i] = INF;
           }
          dfs(1, -1, mid);
          if(dp[1] <= m)
           {
               ans = min(ans, mid);
               right = mid - 1;
           }
         else left = mid+1;
      }
    if(ans == INF)
     printf("-1\n");
    else printf("%d\n", ans);
 }

int main()
{
    while(scanf("%d%d", &n, &m) != EOF)
     {
         if(n == 0 && m == 0)
          break;
         init();
         solve();
     }
    return 0;
}

 

 

 

posted @ 2012-09-26 15:15  Gu Feiyang  阅读(239)  评论(0)    收藏  举报