POJ 2195 (费用流/最小权匹配)

[题目大意]:给出n*m的地图,由几个H(房子)和m(人),求每个人都走到一个房子的最少需要的总步数。

n,m<=100 ;  H==m ;

两种方法,看心情!


1.最小费用最大流 

算出每个人到每个房子的步数; 

s和每个人连边,容量1,费用0

每个人和每个房子连边,容量inf, 费用为步数

每个房子和t连边,容量1,费用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= 20000 ;
const int Max= 100000 ;
const int inf=1<<30 ;
struct node
{
	int u ,v,c,cost,next ;
}edge[Max];
struct node1
{
	int x,y ;
}M[N],H[N] ;
int head[N],vist[N],dist[N],pp[N],pre[N] ;
char g[N][N] ;
int top ;

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 u , v ;    
    memset(vist,0,sizeof(vist));    
    memset(pre,-1,sizeof(pre));    
    for(int i = 0 ; i <= t ; i++)  dist[i]=inf ;    
    vist[s]=1;dist[s]=0;pre[s]=s;    
    queue<int>q;    
    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]=u;    
                     pp[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 mincost=0,flow=0,minflow ;    
    while(SPFA(s,t))    
    {    
          minflow=inf;    
          for(int i=t;i!=s;i=pre[i])    
              minflow=min(minflow,edge[pp[i]].c);    
          for(int i=t;i!=s;i=pre[i])    
          {    
                edge[pp[i]].c -= minflow;    
                edge[pp[i]^1].c += minflow;    
          }        
          flow += minflow;    
          mincost += dist[t]*minflow ;    
        //  printf("****");
    }    
    return mincost ;    
}    
int main()
{ 
  int n,m ;
  while(~scanf("%d%d",&n,&m))
  {  
     if((n+m)==0) break;
        top = 0 ; 
        memset(head,-1,sizeof(head)) ;
        for(int i = 0 ; i < n ; i++)
            scanf("%s",g[i]) ;
        int k=0,k1=0;
        for(int i = 0 ; i < n ; i++)
		   for(int j = 0 ; j < m ; j++)
		     {
		     	    if(g[i][j]=='m')  //人 
		     	      M[k].x=i,M[k++].y=j ;
		     	    if(g[i][j]=='H')  //房子 
		     	       H[k1].x=i,H[k1++].y=j;
		     }    
	    for(int  i = 0 ; i < k ; i++)
		   for(int j = 0 ; j < k1 ; j++)
		     {                 //人到房子的步数 
		     	   int w=abs(M[i].x-H[j].x) + abs(M[i].y-H[j].y) ;
		     	   add(i+1,j+k+1,inf,w) ;
		     }    //k其实等于k1,房子和人的数量相等 
		int s = 0  , t = k+k1+1 ;	 		
	   for(int i  = 0 ; i < k ; i++)
	        add(s,i+1,1,0);
	   for(int i = 0 ; i <k1 ; i++) 
	        add(i+k+1,t,1,0) ;				
		int ans = MFMC(s,t) ;	
		printf("%d\n",ans) ;	
	
    
  }  	
	return 0 ;
} 



2. 最小权匹配 

每个人和房子连边,权值为步数的相反数 ,也就是负的 ;

求一遍最大权匹配, 再把答案变回正数 ;


#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= 200 ;
const int inf=1<<30 ;

struct node1
{
	int x,y ;
}M[N],H[N] ;
int f[N][N] ,lx[N],ly[N],fx[N],fy[N],match[N] ; 
char g[N][N] ;
int k,k1;

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

int KM()
{
	   memset((ly),0,sizeof(ly)) ;
	   memset(match,-1,sizeof(match)) ;
	   for(int i = 1 ; i <= k ;i++) 
	   {
	   	    lx[i]=-inf ;
	   	    for(int j = 1 ; j<=k1 ; j++ )
	   	       lx[i] = max(lx[i],f[i][j]) ;
	   }
	  
	for(int kk = 1 ; kk <= k ; kk++  )
	{
		   while(1)
		   {
		   	         memset(fx,0,sizeof(fx)) ;
		   	         memset(fy,0,sizeof(fy)) ;
		   	         if(dfs(kk))  break;
		   	         int d = inf ;
		   	         for(int  i = 1 ; i <= k ; i++)
		   	            if(fx[i])
		   	              for(int j = 1 ; j <=k1 ;j++)
		   	                if(!fy[j])
							    d = min( d,lx[i]+ly[j]-f[i][j]) ; 
		   	  for(int i  = 1 ; i <= k ; i++)   if(fx[i])  lx[i] -= d ; 
		   	  for(int i = 1 ; i <= k1 ; i++ )  if(fy[i])  ly[i] += d ;
		   	
		   }
	}
	int ans = 0 ;
	for(int i = 1 ;i <= k ; i++)
	  ans += f[match[i]][i] ;
	 return -ans ; 
}


int main()
{ 
  int n,m ;
  while(~scanf("%d%d",&n,&m))
  {  
     if((n+m)==0) break;
        memset(f,0,sizeof(f)) ;
        for(int i = 0 ; i < n ; i++)
            scanf("%s",g[i]) ;
         k=0,k1=0;
        for(int i = 0 ; i < n ; i++)
		   for(int j = 0 ; j < m ; j++)
		     {
		     	    if(g[i][j]=='m')
		     	      M[k].x=i,M[k++].y=j ;
		     	    if(g[i][j]=='H')
		     	       H[k1].x=i,H[k1++].y=j;
		     }    
	    for(int  i = 0 ; i < k ; i++)
		   for(int j = 0 ; j < k1 ; j++)
		     {
		     	   int w=abs(M[i].x-H[j].x) + abs(M[i].y-H[j].y) ;
		     	   f[i+1][j+1]=-w;
		     }
		printf("%d\n",KM()) ;	      
  }  	
	return 0 ;
} 



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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值