算法学社
記錄難忘的征途
posts - 141,comments - 220,trackbacks - 0

题目描述:

   一颗有N个节点(N<2,500)的带权树。现在割去一条边,加到其他节点上,并保证也是一棵树。问最小的直径是多少?

算法分析:

    枚举每条边。树形DP求出割掉这条边后,每个节点可以到的最远距离。
    树形DP是万能的!!!!
#include<iostream>
#include<cstdio>
using namespace std;
// build graph
const int V = 2500 + 5;
const int E = V*2;
int head[V], nxt[E], pnt[E], cost[E], e, n;
void add(int u,int v,int c){
    pnt[e] = v; cost[e] = c;
    nxt[e] = head[u]; head[u] = e; e++;
}
// tree dp
const int inf = ~0u>>2;
int mx[V], sd[V];
void dfs(int u,int f){
    mx[u]= 0; 
    sd[u] = 0;
    for(int i = head[u]; i!=-1; i=nxt[i]) {
        int v = pnt[i];
        if(v == f) continue;
        dfs(v,u);
        if(cost[i] + mx[v] > mx[u]) {
            sd[u] = mx[u];
            mx[u] = cost[i] + mx[v];
        }
        else if(cost[i] + mx[v] > sd[u])
            sd[u] = cost[i] + mx[v];
    }
}
int temp, flag;
void dfs1(int u,int f) {
//    cout<<"u: "<<u<<" "<<mx[u]<<" "<<sd[u]<<endl;
    if(mx[u] < temp) temp = mx[u];
    if(mx[u] > flag) flag = mx[u];
    for(int i = head[u]; i!=-1; i=nxt[i]) {
        int v = pnt[i];
        if(v == f) continue;
//        cout<<"v: "<<v<<" "<<mx[v]<<endl;
        if(mx[v] + cost[i] < mx[u])
            mx[v] = mx[u] + cost[i];
        else if(cost[i] + sd[u] > mx[v]){
            mx[v] = cost[i] + sd[u];
        }
        else if(cost[i] + sd[u] > sd[v]){
            sd[v] = cost[i] + sd[u];
        }
        dfs1(v,u);
    }
}
// main
int main(){
    int test;
    scanf("%d", &test);
    for(int _ = 1; _ <= test; _ ++){
        scanf("%d",&n);
        int u,v,c;
        //init
        for(int i=0;i<n;i++) head[i] = -1;
        e = 0;
        // input
        for(int i=1;i<n;i++) {
            scanf("%d%d%d",&u,&v,&c);
            add(u,v,c);
            add(v,u,c);
        }
        int ans = inf;
        for(int i=0;i<e;i+=2){
            int u = pnt[i], v = pnt[i^1], mn = cost[i], mx1, mx2;
            temp = inf;
            flag = 0;
            dfs(u,v); dfs1(u,v);
            mn += temp;
            mx1 = flag;
//            cout<<temp<<endl;
            temp = inf;
            flag = 0;
            dfs(v,u); dfs1(v,u);
            mn += temp;
            mx2 = flag;
            mn = max(max(mx1,mx2),mn);
//            cout<<temp<<endl;
            if(mn < ans) ans = mn; 
        }
        printf("Case %d: %d\n",_,ans);
    }
}
posted on 2012-07-29 14:57 西月弦 阅读(230) 评论(3)  编辑 收藏 引用

FeedBack:
# re: hdu 3721 树形DP
2012-07-31 09:56 | wuyiqi
你是怎么描述切掉一条边,补到另外两个点上的,没看懂
  回复  更多评论
  
# re: hdu 3721 树形DP
2012-07-31 20:05 | 西月弦
@wuyiqi
比如我要割掉边(u,v),那么我就对以u为根的子树和以v为根的子树分别搜索并树形dp,且保证不通过(u,v)。
那么新的树的直径要么是子树u的直径,要么是子树v的直径。要么是u的最小dp值 + v的最小dp值加(u,v)边权。

ps: 你们多校怎么那么猛。。。 Orz....  回复  更多评论
  
# re: hdu 3721 树形DP
2012-08-01 23:05 | wu
@西月弦
懂了。
我们一队比较强吧,这两次好像都在前10,我的队不是很稳定
  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理