1. 程式人生 > >poj 1853 Cyclic Tour(二分圖拆點+KM演算法)

poj 1853 Cyclic Tour(二分圖拆點+KM演算法)

題目連結:hdu 1853

 

題意:給出n個點,m條邊的帶權有向圖,讓你分成幾個迴圈走完全部點,求最小路徑。

參考部落格:https://blog.csdn.net/u013480600/article/details/38760767

把頂點i拆分成 ii',

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

假設二分圖的完備匹配是1->2’ 2->3’ 3->1’ 6->5’ 5->4’ 4->6’

那麼原圖的有向環為(1->2->3->1) and (6->5->4->6))

 

///二分圖拆點,KM演算法

#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;

#define INF 0x3f3f3f3f

const int maxn=110;
int G[maxn][maxn],match[maxn],lx[maxn],ly[maxn],slack[maxn];
int nx,ny;
bool visx[maxn],visy[maxn];

bool findpath(int x)
{
    int tempdelta;

    visx[x]=1;

    for(int y=0;y<ny;y++)
    {
        if(visy[y]) continue;
        tempdelta=lx[x]+ly[y]-G[x][y];

        if(tempdelta==0){
            visy[y]=1;
            if(match[y]==-1||findpath(match[y])){
                match[y]=x;
                return 1;
            }

        }
         else if(slack[y]>tempdelta)
                slack[y]=tempdelta;
    }
    return 0;
}

void KM()
{
    for(int x=0;x<nx;x++)
    {
        for(int j=0;j<ny;j++) slack[j]=INF;

        while(1)
        {
            memset(visx,0,sizeof(visx));
            memset(visy,0,sizeof(visy));

            if(findpath(x)) break;

            int delta=INF;

            for(int j=0;j<ny;j++)
                if(!visy[j]) delta=min(delta,slack[j]);

            for(int i=0;i<nx;i++)
                if(visx[i]) lx[i]-=delta;

            for(int j=0;j<ny;j++)
            {
                if(visy[j]) ly[j]+=delta;
                else slack[j]-=delta;
            }
        }
    }
}

void solve()
{
    memset(match,-1,sizeof(match));
    memset(ly,0,sizeof(ly));

    for(int i=0;i<nx;i++)
    {
        lx[i]=-INF;
        for(int j=0;j<ny;j++)
            if(lx[i]<G[i][j]) lx[i]=G[i][j];
    }

    KM();
}

int main()
{
    int n,m;

    while(~scanf("%d%d",&n,&m))
    {
        nx=ny=n;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
            G[i][j]=-INF;

        while(m--)
        {
            int x,y,weight;
            scanf("%d%d%d",&x,&y,&weight);///建圖,拆點
            if(-weight>G[x-1][y-1]) G[x-1][y-1]=-weight;        }

        solve();


        int ans=0;
        int num=0;
        for(int j=0;j<ny;j++)
        {
            ///此處的匹配一定不會為-1,因為把-INF當作權值了,故再加個判斷就行了
            if(match[j]==-1||G[match[j]][j]==-INF){
                puts("-1");
                break;
            }
            ans+=G[match[j]][j];
            ++num;
        }
        if(num==ny)
        printf("%d\n",-ans);
    }
    return 0;
}