poj 1853 Cyclic Tour(二分图拆点+KM算法)

版权声明:转载注明下出处就行了。 https://blog.csdn.net/LJD201724114126/article/details/84029972

题目链接: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;
}

猜你喜欢

转载自blog.csdn.net/LJD201724114126/article/details/84029972