nbu 2432 羊群之防

题目链接:http://acm.nbu.edu.cn/v1.0/Problems/Problem.php?pid=2432

 

题目大意:

中文题意,不解释。

 

题目思路:

裸的费用流。

羊进洞所需的时间为费用。

建图:

每只羊对应每个洞的建立一条容量为1的边。

设一个源点,连向每只羊,容量为1。

设一个汇点,每个洞连向汇点,容量为1。

建完图后发现,还是一个二分图,所以我们还可用KM算法,高效又好写~。

 

代码:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

#define ll long long
#define ls rt<<1
#define rs ls|1
#define lson l,mid,ls
#define rson mid+1,r,rs
#define middle (l+r)>>1
#define eps (1e-8)
#define clr_all(x,c) memset(x,c,sizeof(x))
#define clr(x,c,n) memset(x,c,sizeof(x[0])*(n+1))
#define MOD 1000000009
#define INF 0x3f3f3f3f
#define pi acos(-1.0)
#define _max(x,y) (((x)>(y))? (x):(y))
#define _min(x,y) (((x)<(y))? (x):(y))
#define _abs(x) ((x)<0? (-(x)):(x))
#define getmin(x,y) (x= ((x)<0 || (y)<(x))? (y):(x))
#define getmax(x,y) (x= ((y)>(x))? (y):(x))
template <class T> void _swap(T &x,T &y){T t=x;x=y;y=t;}
int TS,cas=1;
const int M=1000+5;
int n,m;
char maze[M][M];
map<pair<int,int>,int>mp;
int vis[M][M],dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
struct node{
	int x,y;
	node(int _x=0,int _y=0){x=_x,y=_y;}
};
vector<node>S,H;

struct KM{
	typedef int type;
	int n,m,match[M];          
	type lx[M],ly[M],w[M][M],d;
	bool vx[M],vy[M];          
	void init(int _n,int _m){
		n=_n,m=_m,clr(match,-1,m);
		for(int i=0;i<n;i++) for(int j=0;j<m;j++) w[i][j]=-INF;
	}
	bool augment(int u){
		vx[u]=1;
		for(int v=0;v<m;v++){
			if(vy[v]) continue;
			if(lx[u]+ly[v]==w[u][v]){
				vy[v]=1;
				if(match[v]==-1 || augment(match[v])){
					match[v]=u;
					return true;
				}
			}else getmin(d,lx[u]+ly[v]-w[u][v]);
		}
		return false;
	}
	type maxWeight(){
		int i,j;
		for(i=0;i<m;i++) ly[i]=0;
		for(i=0;i<n;i++){
			lx[i]=-INF;
			for(j=0;j<m;j++)
				getmax(lx[i],w[i][j]);
		}
		for(i=0;i<n;i++){
			for(;;){
				clr(vx,0,n),clr(vy,0,m);
				d=-1;
				if(augment(i)) break;
				for(j=0;j<n;j++) if(vx[j]) lx[j]-=d;
				for(j=0;j<m;j++) if(vy[j]) ly[j]+=d;
			}
		}
		type res=0;
		for(i=0;i<n;i++) res+=lx[i];
		for(i=0;i<m;i++) res+=ly[i];
		return res;
	}
}p;

void run(){
	int i,j;
	S.clear(),H.clear();
	for(i=1;i<=n;i++){
		scanf("%s",maze[i]+1);
		for(j=1;j<=m;j++)
			if(maze[i][j]=='s')
				S.push_back(node(i,j));
			else if(maze[i][j]=='H')
				H.push_back(node(i,j));
	}
	p.init(S.size(),H.size());
	for(i=0;i<S.size();i++)
		for(j=0;j<H.size();j++)
			p.w[i][j]=-(_abs(S[i].x-H[j].x)+_abs(S[i].y-H[j].y));
	printf("%d\n",-p.maxWeight());
}


void preSof(){
}

int main(){
    //freopen("input.txt","r",stdin);
    //freopen("output.txt","w",stdout);
    preSof();
    //run();
    while(~scanf("%d%d",&n,&m)) run();
    //for(scanf("%d",&TS);cas<=TS;cas++) run();
    return 0;
}


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值