HDU4005 The war [树形DP]

  敌人会在图中加一条边,你的任务是摧毁一条边,使敌人所在块不连通,求最小的花费。

  首先摧毁双联通分量中的边是没有意义的,这个很容易理解。于是对原图进行缩点,行成一棵树,敌人加某一条边后会形成一个环,摧毁这个环中的点是没有意义的,显然对于敌人来说连接叶子节点形成的环会最大。如果这个环不包含最小边,那么去掉最小边后图就不连通了,所以敌人加边后生成的环必然包括最小边。以这个最小边的两个顶点为根生成两棵树,敌人选取了某个叶子节点后,那么选择从这个叶子节点到根节点路径上的边去摧毁都是毫无意义的。对每个节点维护两个值,一个是以该节点为根节点的子树中最小边权,一个是除去这个包含最小边权的子树外的其它子树的最小边权,这个权值是敌人连接最小边权所在子树后能破坏的价值最小的边。最后取次小边中的最小值即可。这个地方有点绕,想了很久,其实自己多画画也就清楚了。

#include <string.h>
#include <stdio.h>
#include <algorithm>
#define INF 0x3fffffff
#define MAXN 10005

struct edge{
    int v,w,n;
}e[MAXN*20],e2[MAXN*20];
int first[MAXN],es,f2[MAXN],es2;
int n,m,tu,tv,tw;
int minl,minr,ans;
void addedge(int u,int v,int w){
    e[es].v=v,e[es].w=w,e[es].n=first[u],first[u]=es++;
}
void addedge2(int u,int v,int w){
   e2[es2].v=v,e2[es2].w=w,e2[es2].n=f2[u],f2[u]=es2++;
}
int low[MAXN],dfn[MAXN],col[MAXN],flg[MAXN*20],vis[MAXN],cols,ind;
void dfs(int u,int f){
    low[u]=dfn[u]=++ind;
    for(int i=first[u];i!=-1;i=e[i].n){
        int v=e[i].v;
        if(v==f)continue;
        if(!dfn[v]){
            dfs(v,u);
            if(low[v]<low[u])low[u]=low[v];
            if(dfn[u]<low[v])flg[i]=flg[i^1]=1;
        }else if(dfn[v]<low[u])low[u]=dfn[v];
    }
}
void color(int u,int clr){
    col[u]=clr,vis[u]=1;
    for(int i=first[u];i!=-1;i=e[i].n)
        if(!vis[e[i].v]&&!flg[i])color(e[i].v,clr);
}
void tarjan(){
    memset(low,0,n*4+10);
    memset(dfn,0,n*4+10);
    memset(col,0,n*4+10);
    memset(flg,0,m*8+10);
    ind=cols=0;
    for(int i=1;i<=n;i++)
        if(!dfn[i])dfs(i,-1);
    memset(vis,0,n*4+10);
    for(int i=1;i<=n;i++)
        if(!vis[i])color(i,++cols);
}
int newgraph(){
    int minedge=INF;
    memset(f2,-1,sizeof f2);es2=0;
    for(int u=1;u<=n;u++){
        for(int i=first[u];i!=-1;i=e[i].n){
            int v=e[i].v;
            if(col[u]==col[v])continue;
            addedge2(col[u],col[v],e[i].w);
            if(e[i].w<minedge)minedge=e[i].w,minl=col[u],minr=col[v];
        }
    }
    return minedge==INF?-1:0;
}
int dp(int u,int f){
   // printf("|%d|\n",u);
    int mins=INF,mins2=INF;
    for(int i=f2[u];i!=-1;i=e2[i].n){
        int v=e2[i].v,w=e2[i].w;
        if(v==f)continue;
        int x=dp(v,u);
        if(x<mins2)mins2=x;
        if(w<mins2)mins2=w;
        if(mins2<mins)std::swap(mins,mins2);
    }
    if(ans>mins2)ans=mins2;
    return mins;
}
int main(){
    freopen("test.in","r",stdin);
    while(scanf("%d%d",&n,&m)!=EOF){
        memset(first,-1,sizeof first);es=0;
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&tu,&tv,&tw);
            addedge(tu,tv,tw);
            addedge(tv,tu,tw);
        }
        tarjan();
        if(newgraph()==-1){printf("-1\n");continue;}
        ans=INF;
        dp(minl,minr);
        dp(minr,minl);
        printf("%d\n",ans==INF?-1:ans);
    }
    return 0;
}
posted @ 2012-08-27 21:44  Burn_E  阅读(223)  评论(0)    收藏  举报