hdu 1853 Cyclic Tour(最小权匹配/费用流)

题意:

    N个城市,M条单向路,Tom想环游全部城市,每次至少环游2个城市,每个城市只能被环游一次。由于每条单向路都有长度,要求游遍全部城市的最小长度。

    // 给定一个有向图,必须用若干个环来覆盖整个图,要求这些覆盖的环的权值最小。

思路:

    原图每个点 u拆为 u u',从源点引容量为 1费用为 0的边到 u,从 u'引相同性质的边到汇点,若原图中存在 (u, v),则从 u 引容量为 1费用为 c(u, v)的边到 v'

    这里源模拟的是出度,汇模拟的是入度,又每个点的出度等于入度等于 1,那么如果最大流不等于顶点数 n,则无解;否则,答案就是最小费用。

 

#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N=500 ;
const int M=10000 ;
const int inf=1<<30 ;
struct node
{
    int u,v,c,cost,next;
}edge[M<<2];
int pre[N],dist[N],vist[N],head[N];
int top,maxflow  ;

void add(int u,int v,int c,int cost)
{
    edge[top].u=u;
    edge[top].v=v;
    edge[top].c=c;
    edge[top].cost=cost;
    edge[top].next=head[u];
    head[u]=top++;
    edge[top].u=v;
    edge[top].v=u;
    edge[top].c=0;
    edge[top].cost=-cost;
    edge[top].next=head[v];
    head[v]=top++;
}

int SPFA(int s,int t,int n)
{
    memset(vist,0,sizeof(vist));
    memset(pre,-1,sizeof(pre));
    int u ,v ;
    queue<int>q;
    for(int i = 0 ; i < n ; i++)  dist[i]=inf ;
    vist[s]=1;dist[s]=0;
    q.push(s);
    while(!q.empty())
    {
           u=q.front();
           q.pop();
           vist[u]=0;
           for(int i = head[u] ; i!=-1 ; i=edge[i].next)
           {
                    v=edge[i].v;
                    if(edge[i].c && dist[v] > dist[u]+edge[i].cost)
                    {
                            dist[v] = dist[u]+edge[i].cost ;
                            pre[v]=i;
                            if(!vist[v])
                            {
                                vist[v]=1;
                                q.push(v);
                            }
                    }
           }
    }
    if(dist[t]==inf) return 0;
    return 1;
}

int MFMC(int s,int t,int n)
{
    int flow=0,mincost=0,minflow;
    while(SPFA(s,t,n))
    {
           minflow=inf;
           for(int i = pre[t] ; i!=-1 ;i=pre[edge[i].u])
                  minflow=min(minflow,edge[i].c);
           for(int i = pre[t] ; i!=-1 ;i=pre[edge[i].u])
            {
                   edge[i].c -=minflow;
                   edge[i^1].c += minflow ;
            }       
           flow += minflow ;
           mincost += dist[t]*minflow ; 
    }
    maxflow=flow ;
    return mincost ;
}

int main()
{
    int n,m,u,v,c;
    while(~scanf("%d%d",&n,&m))
    {
            top=0; 
            memset(head,-1,sizeof(head)); 
            int s=0,t=2*n+1;
            while(m--)
            {
                   scanf("%d%d%d",&u,&v,&c);
                   add(u,v+n,1,c);       
            }
            for(int i = 1 ; i <= n ; i++)
            {
                add(s,i,1,0);
                add(i+n,t,1,0);
            }
            int ans=MFMC(s,t,t+1);
            if(maxflow != n)  printf("-1\n");
            else            printf("%d\n",ans);
    } 
    return 0;
}

 

另一种做法 : 最小权匹配 

 

#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N=500 ;
const int inf=1<<30;
int match[N],lx[N],ly[N],fx[N],fy[N];
int g[N][N];
int n,m;

int dfs(int u)
{
	fx[u]=1;
	for(int i = 1 ; i <= n ; i++)
	{
        if(!fy[i] && lx[u]+ly[i]==g[u][i])
		{
			   fy[i]=1;
			   if(match[i]==-1 || dfs(match[i]))
			   {
			   	       match[i]=u;
			   	       return 1;
			   }
		}	
	}
	return 0;
}

int KM_match()
{
	int a ;
	for(int i = 1 ; i <= n ; i++)
	{
		  lx[i]=-inf;
		  for(int j = 1 ; j <= n ; j++)
		      lx[i]=max(lx[i],g[i][j]);
	}
	memset(match,-1,sizeof(match));
	memset(ly,0,sizeof(ly));
	for(int k = 1 ; k <= n ; k++)
	{
		  while(1)
		  {    
		  	    memset(fx,0,sizeof(fx));
		  	    memset(fy,0,sizeof(fy));
		  	    if(dfs(k))  break ;
		  	     a=inf ;
		  	    for(int i = 1 ; i <= n ; i++)
		  	       if(fx[i])
		  	         for(int j = 1 ; j <= n ; j++)
		  	            if(!fy[j])
		  	              a=min(a,lx[i]+ly[j]-g[i][j]);
		  	    
				  for(int i = 1 ; i <= n ; i++)
				  {
				  	     if(fx[i])  lx[i] -= a ;
				  	     if(fy[i])  ly[i] += a ;
				  }          
		  }
	}
	int sum=0;
	for(int i = 1 ; i <= n ;i++)
	{
		  if(match[i]==-1 || g[match[i]][i]==-inf)  { sum=-1 ; return sum ;}
		  sum += g[match[i]][i] ;
	}
	return -sum ;
}

int main()
{
	int a,b,w;
	while(~scanf("%d%d",&n,&m))
	{ 
	    for(int i = 1 ; i <= n ; i++)
	         for(int j = 1 ; j <= n ; j++)
	            g[i][j]=-inf ;
		   while(m--)
		   {
		   	    scanf("%d%d%d",&a,&b,&w);
		   	    if(g[a][b] <-w)
		   	      g[a][b]=-w;		   	
		   }	
		   int ans=KM_match();
		   printf("%d\n",ans);
	}
	return 0;
}


 


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值