hdu(3488)Tour

这个题要用KM算法来计算最大权值,但这里要算最小的,,则需要进行转化,

将map初始化到负无穷,然后map存负的权值,这样小的距离一变负的,就成大的了,就可以用,KM了,最后取负值便可以了。。

另外也可以用以大数减小变大数来利用KM计算。。

 

题意:给你一个有向图,边有权值,现在要你求若干个环包含所有的顶点,并且每个顶点只出现一次(除了一个环中的起始点)使得环中所有边得权值之和最小。

分析:因为每个顶点只出现一次,那么每个顶点只关联两个顶点入度顶点和出度顶点,所以构造二分图,将一个点u拆成u,u'。那么对于这个二分图如果存在着完美匹配的话,那么原图中一定存在若干个环,环中包含每个顶点,对于权值之和最小,只需求最小权匹配即可。

 #include<stdio.h>
#include<string.h>
#define inf 9999999
int sx[1000],sy[1000];
int lx[1000],ly[1000];
int map[1000][1000];
int n,m,a,mark[1000],link[1000];
int find(int k)
{
 int i;
 sx[k]=1;
 for(i=1;i<=n;i++)
 {
  if(sy[i]==0&&lx[k]+ly[i]==map[k][i])
  {
   sy[i]=1;
   if(link[i]==-1||find(link[i]))
   {
    link[i]=k;
    return 1;
   }
  }
 }
 return 0;
}
int  KM()
{
 int i,j,k,sum=0;
 memset(lx,0,sizeof(lx));
 memset(ly,0,sizeof(ly));
 for(i=1;i<=n;i++)
  for(j=1;j<=n;j++)
   if(lx[i]<map[i][j])
    lx[i]=map[i][j];
   for(i=1;i<=n;i++)
   {
    while(1)
    {
     memset(sx,0,sizeof(sx));
     memset(sy,0,sizeof(sy));
    
     if(find(i))
     break; 
     a=inf;
     for(j=1;j<=n;j++)
     {
      if(sx[j])
      {
        for(k=1;k<=n;k++)
         if(!sy[k])
         {
          if(a>lx[j]+ly[k]-map[j][k])
           a=lx[j]+ly[k]-map[j][k];
         }
      }
     }
     for(j=1;j<=n;j++)
     {
      if(sx[j])
       lx[j]-=a;
      if(sy[j])
       ly[j]+=a;
     }
    }
   }
   for(i=1;i<=n;i++)
    sum+=map[link[i]][i];
   return sum;
}
int main()
{
    int i,j,a,b,c;
 int k;
 scanf("%d",&k);
 while(k--)
 {
           scanf("%d%d",&n,&m);
            memset(link,-1,sizeof(link));
            for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
                map[i][j]=-inf;
          for(i=0;i<m;i++)
    {
            scanf("%d%d%d",&a,&b,&c);
            if(-c>map[a][b])
            map[a][b]=-c;
    }
              printf("%d\n",-KM());
     }
    return 0;
}


 

 

 

 

#include<stdio.h>
#include<string.h>
#define inf 0x7fffffff
int map[301][301];
int lx[301],ly[301];
int sx[301],sy[301];
int link[301],n,a;
int www=300005;
int find(int k)
{
    int i;
    sx[k]=1;
    for(i=1;i<=n;i++)
    {
        if(sy[i]==0&&lx[k]+ly[i]==map[k][i])
        {
            sy[i]=1;
            if(link[i]==-1||find(link[i]))
            {
                link[i]=k;
                return 1;
            }
        }
    }
    return 0;
}
int MX()
{
    int sum=0,i,j,k;
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            if(lx[i]<map[i][j])
                lx[i]=map[i][j];
            for(i=1;i<=n;i++)
            {
                while(1)
                {
                    a=inf;
                    memset(sx,0,sizeof(sx));    
                    memset(sy,0,sizeof(sy));
                    if(find(i))
                        break;                   
                        for(j=1;j<=n;j++)
                            if(sx[j]==1)
                            {
                                for(k=1;k<=n;k++)
                                    if(sy[k]==0)
                                    {
                                        if(a>lx[j]+ly[k]-map[j][k])
                                            a=lx[j]+ly[k]-map[j][k];
                                    }
                            }
                            for(j=1;j<=n;j++)
                            {
                                if(sx[j]==1)
                                    lx[j]-=a;
                                if(sy[j]==1)
                                    ly[j]+=a;
                            }
                }
            }
   for(i=1;i<=n;i++)
      sum+=www-map[link[i]][i];
 return sum;
}
int main()
{
    int i,k,m;
int a,b,c;
scanf("%d",&k);
while(k--)
{
scanf("%d%d",&n,&m);
        memset(lx,0,sizeof(lx));
        memset(ly,0,sizeof(ly));
        memset(link,-1,sizeof(link));
memset(map,0,sizeof(map));
for(i=0;i<m;i++)
{
scanf("%d%d%d",&a,&b,&c);
if(www-c>map[a][b])
map[a][b]=www-c;
}
            printf("%d\n",MX());
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值