HDUOJ 2121 Ice_cream’s world II(不定根的最小樹形圖-好題)

題目連接~~~

  本題是求不定根的情況,如果以每個點爲根來一次最小樹形圖,那必定超時。這裏我們可以虛擬出來一個點作爲根,然後,讓這個點連接所有頂點,那邊的權值是多少呢?就是所有權值和加1,這樣保證瞭如果最小樹形圖存在,那麼只有一條虛擬的邊加入到最小樹形圖中,讓總和減去它就可以了,還有一種情況就是如果原先圖不存在最小樹形圖,那麼有可能選擇兩條虛擬邊加入最小樹形圖,只要判斷一下,最後的值是否大於等於原先權值和加1的二倍,是則無解,否則存在最小樹形圖。那怎麼求最小樹形圖在原先圖中的根節點呢?通過上面我們知道,最後只有一個虛擬邊x加入最小樹形圖,而這個邊是加給第x-m點的邊,所以在圖不斷變化的過程中只要記住虛擬邊的邊號x,然後x-m就是答案。

代碼:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <algorithm>
using namespace std ;
#define INF 0x3f3f3f3f
const double esp = 0.00000001 ;
const int MX = 20000 + 10 ;
const int MY = 1e3 + 10 ;

int tot ,f_num ;
typedef struct {
    int u ,v ,w ;
    int next ;
}Edge ;
Edge E[MX] ;
int head[MY] ,In[MY] ,pre[MY] ,Id[MY] ,vis[MY] ;
void Add_edge(int u ,int v ,int w){
    E[tot].u = u ; E[tot].v = v ; E[tot].w = w ; E[tot].next = head[u] ; head[u] = tot++ ;
}
int Zhuliu(int root ,int n ,int m ,Edge E[]){
    int u ,v ,w ,ret = 0 ;
    while(1){
        for(int i = 0 ;i < n ; ++i)
            In[i] = INF ;
        for(int i = 0 ;i < m ; ++i){
            u = E[i].u ; v = E[i].v ; w = E[i].w ;
            if(u != v && w < In[v]){
                In[v] = w ;
                pre[v] = u ;
                if(u == root) f_num = i ;
            }
        }
        for(int i = 0 ;i < n ; ++i){
            if(i != root && In[i] == INF)
                return -1 ;//有孤立的點,沒有解
        }
        int nt = 0 ;
        memset(Id ,-1 ,sizeof(Id)) ;
        memset(vis ,-1 ,sizeof(vis)) ;
        In[root] = 0 ;
        for(int i = 0 ;i < n ; ++i){
            ret += In[i] ;
            v = i ;
            while(vis[v] != i && Id[v] == -1 && v != root){
                vis[v] = i ;
                v = pre[v] ;
            }
            if(v != root && Id[v] == -1){
                for(int u = pre[v] ; u != v ; u = pre[u])
                    Id[u] = nt ;
                Id[v] = nt++ ;
            }
        }
        if(nt == 0) break ;//沒有有向環
        for(int i = 0 ;i < n ; ++i)
            if(Id[i] == -1)
                Id[i] = nt++ ;
        for(int i = 0 ;i < m ; ++i){
            v = E[i].v ;
            E[i].u = Id[E[i].u] ;
            E[i].v = Id[E[i].v] ;
            if(E[i].u != E[i].v)
                E[i].w -= In[v] ;
        }
        n = nt ;
        root = Id[root] ;
    }
    return ret ;
}
int main(){
    //freopen("input.txt" ,"r" ,stdin) ;
    int n , m ;
    while(~scanf("%d%d" ,&n ,&m)){
        tot = 0 ; f_num = 0 ;
        int sum = 1 ,u ,v ,w ;
        memset(head ,-1 ,sizeof(head)) ;
        for(int i = 0 ;i < m ; ++i){
            scanf("%d%d%d" ,&u ,&v ,&w) ;
            if(u != v){
                Add_edge(u ,v ,w) ;
            }
            sum += w ;//把所有的權值都加起來
        }
        for(int i = 0 ;i < n ; ++i){//再加上n條邊
            Add_edge(n ,i ,sum) ;
        }
        int ans = Zhuliu(n ,n+1 ,n+m ,E) ;
        if(ans == -1 || ans >= sum*2) printf("impossible\n") ;
        else printf("%d %d\n" ,ans-sum ,f_num - m) ;
        puts("") ;
    }
    return 0 ;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章