hdu3586 Information Disturbing 树形DP+二分
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3586
题目大意:给定n个敌方据点,编号1为司令部,其他点各有一条边相连构成一棵树,每条边都有一个权值cost表示破坏这条边的费用,叶子节点为前线。现要切断前线和司令部的联系,每次切断边的费用不能超过上限limit,问切断所有前线与司令部联系所花费的总费用少于m时的最小limit。1<=n<=1000,1<=m<=100万。
思路:很容易想到DP,定义dp[root]表示以root为根节点的子树失去与其叶子节点的链接的最小花费,那么如果其与孩子的边权小于limit,则dp[root]+=min(dp[son],w)(son为root的孩子,w为相连的边的权值),否则的话,dp[root]+=dp[son];
那么怎样确定limit值呢,由于问题是求使总花费不超过m的最小的limit值,当然就是二分答案了,确定limit的值的下限为1,上限为最大的边值,然后对于limit进行二分查找,如果最后求的总的花费值小于等于m,则往前找,否则就往后找。
代码如下:
1 #include<cstdlib> 2 #include<cstdio> 3 #include<cstring> 4 #include<iostream> 5 using namespace std; 6 #define MAX 1010 7 #define INF 1000010 8 class node 9 { 10 public: 11 int to; 12 int w; 13 int next; 14 }; 15 node edge[2*MAX]; 16 int head[MAX]; 17 int n,m; 18 int tol; 19 int dp[MAX]; 20 int vis[MAX]; 21 void init() 22 { 23 tol=0; 24 memset(head,-1,sizeof(head)); 25 } 26 void Build_Tree(int u,int v,int w) 27 { 28 edge[tol].to=v; 29 edge[tol].w=w; 30 edge[tol].next=head[u]; 31 head[u]=tol++; 32 } 33 int low,high,mid; 34 void dfs(int root,int pre) 35 { 36 vis[root]=1; 37 int flag=0; 38 for(int i=head[root];i!=-1;i=edge[i].next) 39 { 40 if(vis[edge[i].to]) continue; 41 flag=1; 42 int cost=edge[i].w; 43 int son=edge[i].to; 44 dfs(son,root); 45 if(cost<=mid) dp[root]+=min(dp[son],cost); 46 else dp[root]+=dp[son]; 47 } 48 if(flag==0) dp[root]=INF; 49 } 50 int main() 51 { 52 while(scanf("%d%d",&n,&m)!=EOF) 53 { 54 init(); 55 if(n==0&&m==0) break; 56 low=1;high=0; 57 58 for(int i=1;i<n;i++) 59 { 60 int u,v,w; 61 scanf("%d%d%d",&u,&v,&w); 62 if(high<w) high=w; 63 Build_Tree(u,v,w); 64 Build_Tree(v,u,w); 65 } 66 int ans=-1; 67 while(low<=high) 68 { 69 memset(dp,0,sizeof(dp)); 70 memset(vis,0,sizeof(vis)); 71 mid=(low+high)/2; 72 dfs(1,-1); 73 if(dp[1]>m) low=mid+1; 74 else {high=mid-1;ans=mid;} 75 } 76 cout<<ans<<endl; 77 } 78 return 0; 79 }