题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3586
题目大意:给定n个敌方据点,1为司令部,其他点各有一条边相连构成一棵树,每条边都有一个权值cost表示破坏这条边的费用,叶子节点为前线。现要切断前线和司令部的联系,每次切断边的费用不能超过上限limit,问切断所有前线与司令部联系所花费的总费用少于m时的最小limit。1<=n<=1000,1<=m<=100万
解题思路:一看到题目就觉得是树形DP,男人的第六感怎一个准字了得。然后努力地把题目看懂了以后,发现这是一个判定性问题,就是问某个limit是否能够满足条件切断所有前线与司令部的联系,然后找符合条件的最大limit就可以了。想通这点以后就可以二分答案,下限为1,上限为最大的边权,再写一个Tree_DP(limit),判断切断所有前线联系时花费的最小费用是否小等于m,是的话就往后查找,否则向前。
设dp[i]为切断i的所有子孙叶子所花费的最小费用,状态转移方程: if (i->son.len < limit) dp[i] += min(dp[i->son],i->son.len); (如果与子节点相连的边可选)
else dp[i] += dp[i->son];(如果与子节点相连的边不可选)
测试数据:
4 2
1 2 3
2 3 1
2 4 1
5 5
1 3 2
1 4 3
3 5 5
4 2 6
5 10
1 3 5
1 4 5
3 5 5
4 2 5
5 5
1 2 5
2 3 5
2 4 5
3 5 5
2 1
1 2 1
代码:
#include <stdio.h>
#include <string.h>
#define MIN 1100
#define MAX 2100
#define INF 1000001
#define min(a,b) (a)<(b)?(a):(b)
struct node {
int v,len;
node *next;
}*head[MAX],tree[MAX];
int n,m,dp[MIN];
int high,low,mid,len;
int ptr,vis[MIN],ans;
void Initial() {
ans = INF,len = 0,ptr = 1;
memset(head,NULL,sizeof(head));
}
void AddEdge(int a,int b,int c) {
tree[ptr].v = b,tree[ptr].len = c;
tree[ptr].next = head[a],head[a] = &tree[ptr++];
tree[ptr].v = a,tree[ptr].len = c;
tree[ptr].next = head[b],head[b] = &tree[ptr++];
}
void Tree_DP(int v,int maxx) {
if (vis[v]) return;
vis[v] = 1;
int i,j,k,tpk;
node *p = head[v];
int tot = 0,flag = 0;
while (p != NULL) {
if (!vis[p->v]) {
flag = 1;
Tree_DP(p->v,maxx);
if (p->len > maxx) tot += dp[p->v];
else tot += min(dp[p->v],p->len);
}
p = p->next;
}
if (flag) dp[v] = tot;
else dp[v] = INF;
}
int main()
{
int i,j,k,a,b,c;
while (scanf("%d%d",&n,&m),n+m) {
Initial();
for (i = 1; i < n; ++i) {
scanf("%d%d%d",&a,&b,&c);
AddEdge(a,b,c);
if (c > len) len = c;
}
low = 1,high = len;
while (low <= high) {
for (i = 0; i <= n; ++i)
dp[i] = vis[i] = 0;
mid = low + (high - low)/2;
Tree_DP(1,mid);
if (dp[1] <= m) {
ans = mid;
high = mid - 1;
}
else low = mid + 1;
}
if (ans == INF) printf("-1\n");
else printf("%d\n",ans);
}
return 0;
}
本文ZeroClock原创,但可以转载,因为我们是兄弟。