现在的位置: 首页 > 综合 > 正文

hdu 3488 Tour(二分匹配 最小权完备匹配 km算法)

2017年07月15日 ⁄ 综合 ⁄ 共 1411字 ⁄ 字号 评论关闭

WA了好多次,都找不出什么错误,最后队友看了disscuss才知道,原来连接两个顶点的边不止一条,应该取最小的,但是从题意中没有读出来,被虐了,悲剧呀!!!~~~~

题目大体意思是求n个环的并,每个点只能在一个环里,找到可以遍历所有顶点的边的最小值,显然每个点只能关联其他两个顶点,并且入度和初度都为1,所以应该是完全匹配,完全匹配图就是n个环的并,所以求的是完美匹配中的最小权问题,用km解决即可~

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const int inf=10000000;
const int N=205;
int g[N][N]; //邻接矩阵
int lx[N],ly[N]; //顶点标号
bool sx[N],sy[N];//是否已经搜索过,S为寻找从i出发的增广轨时访问的x中的点的集合,T为访问的y中的点的集合。
int link[N];
int n;
bool path(int k)  //从x[k]寻找增广路
{
    int i;
    sx[k]=true;
    for(i=0; i<n; i++)
    {
        if(!sy[i]&&(lx[k]+ly[i]==g[k][i]))//相等子图
        {
            sy[i]=1;
            if(link[i]==-1||path(link[i]))
            {
                link[i]=k;
                return true;
            }
        }
    }
    return false;
}
int BestMatch() //求解最小权匹配
{
    int i,j,k,d,sum;
    memset(ly,0,sizeof(ly));//初始化y的顶点标号
    for(i=0; i<n; i++)
    {
        lx[i]=-1;//x中顶点i的编号为与i关联的Y中边的最大权重
        for(j=0; j<n; j++)
            if(lx[i]<g[i][j])
                lx[i]=g[i][j];//初始化x的顶点标号
    }
    memset(link,-1,sizeof(link));
    for(k=0; k<n; k++)
    {
        while(1)
        {
            memset(sx,0,sizeof(sx));
            memset(sy,0,sizeof(sy));
            if(path(k))  //匹配成功
                break;
            d=inf;
            for(i=0; i<n; i++)
            {
                if(sx[i]) //x在交错树中
                {
                    for(j=0; j<n; j++)
                    {
                        if(!sy[j])//y不在交错树中,扩大子图
                            d=min(d,lx[i]+ly[j]-g[i][j]);
                    }
                }
            }//若匹配不成功,则修改顶点标号,找到d的值
            for(i=0; i<n; i++)
            {
                if(sx[i])
                    lx[i]-=d;//修改x顶点标号
                if(sy[i])
                    ly[i]+=d;//修改y顶点标号
            }
        }
    }
    sum=0;
    for(i=0; i<n; i++)
        sum+=g[link[i]][i];
    sum*=-1;
    return sum;
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int m,ans,i,j;
        cin>>n>>m;
        for(i=0; i<n; i++)
            for(j=0; j<n; j++)
                g[i][j]=-inf;//即将边的权值取反
        while(m--)
        {
            int u,v,w;
            cin>>u>>v>>w;
            if(g[--u][--v]<-w)//WA的地方,注意呀!!!
                g[u][v]=-w;
        }
        ans=BestMatch();
        printf("%d\n",ans);
    }
    return 0;
}

抱歉!评论已关闭.