1. 程式人生 > >HDU 1853 & HDU 3488【有向環最小權值覆蓋問題 】帶權二分圖匹配 KM演算法

HDU 1853 & HDU 3488【有向環最小權值覆蓋問題 】帶權二分圖匹配 KM演算法

In the kingdom of Henryy, there are N (2 <= N <= 200) cities, with M (M <= 30000) one-way roads connecting them. You are lucky enough to have a chance to have a tour in the kingdom. The route should be designed as: The route should contain one or more loops. (A loop is a route like: A->B->……->P->A.)  Every city should be just in one route.  A loop should have at least two cities. In one route, each city should be visited just once. (The only exception is that the first and the last city should be the same and this city is visited twice.)  The total distance the N roads you have chosen should be minimized. 

Input

An integer T in the first line indicates the number of the test cases.  In each test case, the first line contains two integers N and M, indicating the number of the cities and the one-way roads. Then M lines followed, each line has three integers U, V and W (0 < W <= 10000), indicating that there is a road from U to V, with the distance of W.  It is guaranteed that at least one valid arrangement of the tour is existed.  A blank line is followed after each test case.

Output

For each test case, output a line with exactly one integer, which is the minimum total distance.

Sample Input

1
6 9
1 2 5
2 3 5
3 1 10
3 4 12
4 1 8
4 6 11
5 4 7
5 6 9
6 5 4

Sample Output

42

題意:

給出n個點m條單向邊邊以及經過每條邊的費用,讓你求出走過一個哈密頓環(除起點外,每個點只能走一次)的最小費用。

有向環最小權值覆蓋問題可以使用KM演算法(帶權二分圖匹配演算法)

我們把任意一個頂點i都分成兩個,即i和i’. 如果原圖存在i->j的邊,那麼二分圖有i->j’的邊.

       下面我們要引出幾條結論:

      1. 如果原圖能由多個不相交的有向環覆蓋,那麼二分圖必然存在完備匹配.

(假設原圖的有向環為(1->2->3->1) and(6->5->4->6),那麼二分圖的完備匹配就是1->2’ 2->3’ 3->1’6->5’ 5->4’ 4->6’)

       2.如果二分圖存在完備匹配,那麼原圖必定能由幾個不相交的有向環覆蓋.

(假設二分圖的完備匹配是1->2’ 2->3’ 3->1’ 6->5’ 5->4’ 4->6’那麼原圖的有向環為(1->2->3->1) and (6->5->4->6))

       3.如果原圖存在權值最大的有向環覆蓋,那麼二分圖的最優匹配一定就是這個值.

(因為該有向環覆蓋對應了一個二分圖的完備匹配,而該完備匹配的權值就等於該有向環覆蓋的權值,所以最優匹配不可能丟失該最大權值的匹配)

有解:

       現在原題要求的是最小長度匹配,我們把所有已知邊的權值都取負數,且那些不存在的邊我們取-INF(負無窮). 如果完備匹配存在,那麼我們求出的最優匹配權值的絕對值 肯定<INF. 且該絕對值就是最小權值匹配.

無解:

       如果完備匹配不存在,那麼最優匹配權值的絕對值 肯定>INF, 或者這麼說,如果最終求得的匹配中,有任何一個匹配邊用了權值為負無窮的邊,那麼最優匹配不存在(即完備匹配不存在)

       注意:此題輸入可能存在重邊.

#include<cstdio>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f
const int maxn=1e4+10;
int n,m;
int w[maxn][maxn];
int lx[maxn],ly[maxn];
int link[maxn];
int slack[maxn];
int visx[maxn],visy[maxn];
bool dfs(int x)
{
    visx[x]=1;
    for(int y=1;y<=n;y++){
        if(visy[y]) continue;
        int t=lx[x]+ly[y]-w[x][y];
        if(t==0){
            visy[y]=1;
            if(link[y]==0||dfs(link[y])){
                link[y]=x;
                return 1;
            }
        }
        else if(slack[y]>t) slack[y]=t;
    }
    return 0;
}
int km()
{
    memset(lx,0,sizeof lx);
    memset(ly,0,sizeof ly);
    memset(link,0,sizeof link);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        if(lx[i]<w[i][j])
        lx[i]=w[i][j];
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++)
            slack[j]=INF;
        while(1){
            memset(visx,0,sizeof visx);
            memset(visy,0,sizeof visy);
            if(dfs(i))  break;
            int d=INF;
            for(int k=1;k<=n;k++)
                if(!visy[k]&&d>slack[k])
                d=slack[k];
            for(int k=1;k<=n;k++){
                if(visx[k]) lx[k]-=d;
                if(visy[k]) ly[k]+=d;
            }
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++){
        //if(w[link[i]][i]==-INF) return -1;
        ans+=w[link[i]][i];
    }

    return -ans;
}
int main()
{
        int T;
    scanf("%d",&T);
    while(T--){
          scanf("%d%d",&n,&m);
        int u,v,cost;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            w[i][j]=-INF;

        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&u,&v,&cost);
            cost=-cost;
            if(w[u][v]!=-INF)//去重邊,取權值最小的邊建圖
                cost=max(cost,w[u][v]);
            w[u][v]=cost;
            //w[v][u]=cost;
        }
            printf("%d\n",km());
    }
}