poj 2195 Going Home(KM||费用流)

http://poj.org/problem?id=2195


题目大意:

给定一个N*M的地图,地图上有一定数量的m(代表人)和H(代表房子),两者数量相等;

要求求出把所有m移动到H的位置上总距离最小为多少;


思路:

用最优二分匹配或者最小费用最大流都能做,觉得用最优二分匹配会简单一些;

建图把m和H分别作为二分图的两个顶点集合vx和vy,求出两两之间的距离,用km算法求出最优匹配;

要注意的是,要求距离的最短,而km求的是最大匹配值,所以建图的时候距离要取反;


这是KM做的,费用流做的在下面;

#include<iostream>
#include<cmath>
using namespace std;

const int MAX=105;
const int inf=1<<30;

int n,lack;
int map[MAX][MAX]; 
char maps[MAX][MAX];
int dx[MAX],dy[MAX]; 
int link[MAX];
bool visx[MAX],visy[MAX]; 

bool DFS(int v)
{
    visx[v]=true;
    for(int i=0;i<n;i++)
    {
        if(visy[i])
            continue;
        int t=dx[v]+dy[i]-map[v][i];
        if(!t)
        {
            visy[i]=true;
            if(link[i]==-1||DFS(link[i]))
            {
                link[i]=v;
                return true;
            }
        }
        else 
            if(t<lack)
                lack=t;
    }
    return false;
}

void KM()
{
    int i,j;
    memset(dx,0,sizeof(dx));
    memset(dy,0,sizeof(dy));
    memset(link,-1,sizeof(link));
    for(i=0;i<n;i++)
    {
        for(j=0;j<n;j++)
            if(map[i][j]>dx[i])
                dx[i]=map[i][j];
    }
    for(i=0;i<n;i++)
    {
        while(true)
        {
            memset(visx,0,sizeof(visx));
            memset(visy,0,sizeof(visy));
            lack=0x7fffffff;
            if(DFS(i))
                break;
            for(j=0;j<n;j++)
            {
                if(visx[j])
                    dx[j]-=lack;
                if(visy[j])
                    dy[j]+=lack;
            }
        }
    }
}

int main(int i,int j,int k,int l)
{
    int row,col,ans,numi,numj;
    while(scanf("%d%d",&row,&col)&&(row+col))
    {
		/*Initial*/
        n=ans=numi=numj=0;
        memset(link,-1,sizeof(link));
        memset(map,0,sizeof(map));

		/*Input*/
        for(i=0;i<row;i++)
        {
            scanf("%*c");
            for(j=0;j<col;j++)
            {
                scanf("%c",&maps[i][j]);
                if(maps[i][j]=='m')
                    n++;
            }
        }
		/*Structure Graph*/
		/*±ßȨֵ±äΪ¸ºÈ¨£¬Çó³ö×î´óÆ¥ÅäÖµ¼´Îª×îС¾àÀë×ܺÍ*/

        for(i=0;i<row;i++) 
        {
            for(j=0;j<col;j++)
            {
                if(maps[i][j]=='m')
                {
                    for(k=0;k<row;k++)
                    {
                        for(l=0;l<col;l++)
                        {
                            if(maps[k][l]=='H')
                                map[numi][numj++]=-(abs(k-i)+abs(l-j)); //±ßΪ¸ºÈ¨Öµ
                        }
                    }
                    numi++;
                    numj=0;
                }
            }
        }
        KM();
        int ans=0;    
        for(i=0;i<n;i++)
        {
            if(link[i]!=-1)
                ans+=map[link[i]][i];
        }

        printf("%d\n",-ans);
    }
    return 0;
}


-------------------------------------------------华丽的分割线------------------------------------------

#include<iostream>
#include<queue>
#include<cmath>
#include<cstring>
using namespace std;

const int inf=1<<30;
const int N=100500;
const int M=200500;

class Edge
{
public:
	int u;
	int v;
	int cost;
	int flow;
	int next;
}edge[M<<3];

class Node
{
public:
	int x;
	int y;
}man[105],house[105];

int s,t;
int edge_num;
int max_flow;
int head[N];
int pre[N];
int dist[N];
bool vis[N];
char map[105][105];

void addedge(int u,int v,int cost,int flow)
{
	edge[edge_num].u=u;
	edge[edge_num].v=v;
	edge[edge_num].flow=flow;
	edge[edge_num].cost=cost;	
	edge[edge_num].next=head[u];
	head[u]=edge_num++;
	
	edge[edge_num].u=v;
	edge[edge_num].v=u;
	edge[edge_num].flow=0;
	edge[edge_num].cost=-cost;
	edge[edge_num].next=head[v];
	head[v]=edge_num++;
	return ;
}

/*SPFA 对费用求最短路*/
bool spfa()
{
	queue <int> que;

	memset(pre,-1,sizeof(pre));
	memset(vis,false,sizeof(vis));
	for(int j=0;j<=t;j++)
		dist[j]=inf;
	dist[s]=0;
	vis[s]=true;
	
	que.push(s);
	
	while(!que.empty())
	{
		int x=que.front();
		que.pop();
		int y;
		
		for(int i=head[x];~i;i=edge[i].next)
		{
			y=edge[i].v;
			if(edge[i].flow && dist[y]>dist[x]+edge[i].cost)
			{
				dist[y]=dist[x]+edge[i].cost;
				pre[y]=i;
				
				if(!vis[y])
				{
					vis[y]=true;
					que.push(y);
				}
			}
		}
		vis[x]=false;
	}
	if(dist[t]==inf)	return false;
	return true;
}

void argument_flow()
{

	int v,alfa=inf; 
	for(v=pre[t];~v;v=pre[ edge[v].u ])
	{
		if(alfa>edge[v].flow)
			alfa=edge[v].flow;
	}
	for(v=pre[t];~v;v=pre[ edge[v].u ])
	{
		edge[v].flow -= alfa;
		edge[v^1].flow += alfa;	
	}
	max_flow += alfa*dist[t];
}

int main(int i,int j)
{
	int n,m;
	while(~scanf("%d %d",&n,&m)&&n&&m)
	{
		edge_num=0;
		max_flow=0;
		s=0;
		t=n*m+1;
		memset(head,-1,sizeof(head));
		
		int p=0;
		int q=0;

		/*Input & Structure Graph*/
		for(i=1;i<=n;i++)
		{
			scanf("%s",map[i]+1);
			for(j=1;j<=m;j++)
			{
				int x=(i-1)*m+j;	
				if(map[i][j]=='m')
				{
					addedge(s,x,0,1);
					man[++p].x=i;//储存人的坐标
					man[p].y=j;
				}
				if(map[i][j]=='H')
				{
					addedge(x,t,0,1);
					house[++q].x=i;//储存房子的坐标
					house[q].y=j;
				}
			}
		}
		for(i=1;i<=p;i++)
		{
			/*人和房子之间连边*/
			for(j=1;j<=q;j++)
				addedge(man[i].x*m+man[i].y-m,house[j].x*m+house[j].y-m,abs(man[i].x-house[j].x)+abs(man[i].y-house[j].y),1);
		}
		while(spfa())
			argument_flow();

		printf("%d\n",max_flow);
	}
	return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值