BZOJ 2547(匈牙利算法-任意边的处理)

2547: [Ctsc2002]玩具兵

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 104   Solved: 50
[ Submit][ Status][ Discuss]

Description

小明的爸爸给他买了一盒玩具兵,其中有 K个步兵,K个骑兵和一个天兵,个个高大威猛,形象逼真。盒子里还有一个M*N棋盘,每个格子(i,j)都有一个高度Hij,并且大得足以容纳所有的玩具兵。小明把所有的玩具兵都放到棋盘上去,突然想到了一种很有趣的玩法:任意挑选T个不同的格子,并给每个格子i规定一个重要值Ri­­,游戏的目标就是每次沿东南西北之一的方向把一个玩具兵移动到其相邻的格子中(但不能移动到棋盘外面去),最终使得每个挑选出的格子i上恰好有Ri个玩具兵。小明希望所有的玩具兵都在某个选定的格子中,因此他总是使选出的T个格子的重要值之和等于玩具兵的个数。为了增加难度,小明给玩具兵们的移动方式做了一些规定:

  步兵只会往高处爬,因此如果两个格子AB相邻,当且仅当格子A的高度小于或等于B,步兵才可以从A移动到B

      骑兵只会往低处跳,因此如果两个格子AB相邻,当且仅当格子A的高度大于或等于B,骑兵才可以从A移动到B

      天兵技术全面,移动不受任何限制。

       可是没玩几次,小明就发现这个游戏太难了,他常常玩了好半天也达不到目的。于是,他设计了一种“超能力”,每使用一次超能力的时候,虽然不能移动任何一个玩具兵,但可对它们进行任意多次交换操作,每次交换两个玩具兵。等这次超能力使用完后又可和平常一样继续移动这些玩具兵。借助强大的超能力,这个游戏是容易玩通的,但是怎样才能让使用超能力的次数最少呢?

 

Input

第一行包含四个整数:M,N,K,T (2<=M,N<=100, 1<=K<=50, 1<=T<=2K+1)

    第二行包括2K+1个数对(xi,yi),代表各个玩具兵的初始位置。前K个代表兵,接下来的K个代表骑兵,最后一个代表兵。

    第三行包含T个三元组(xi,yi,ri),第i组代表第i个目标格的位置和重要值。

        以下M行,每行N个整数。其中第i行第j个数为即格子的高度Hij。高度是不超过100的正整数,注意:不同玩具兵的初始位置可能相同。输入数据保证无错,选定的T个格子的重要值之和保证等于2K+1

Output

仅包含一行,即使用超能力的最小次数T

Sample Input

4 6 2 5
1 1 1 5 4 1 4 5 3 3
1 2 1 2 6 1 3 2 1 3 6 1 4 3 1
3 2 6 1 3 5
2 1 7 4 4 6
2 3 1 4 3 4
4 3 4 3 2 3

Sample Output

1


先来分析一下这个问题。
1.显然天兵的初始位置不重要。
2.最坏情况不断使用天兵,2*k次便能解决.

不妨假设不存在天兵:
1.我们把调换位置-改为调换兵种(显然成立),那么一个棋子一定调换(否则移到需要的位置调换).
2.显然如果我有确定的‘超能力’次数,那么任何一个棋子所能到达的地方有限(bfs)。
3.显然我们可以判断1个棋子在超能力步数内必须到达另一个棋子(就是这个棋子,无论怎么调换兵种).
问是否是合法方案   
那么这就是一个二分图匹配,必须完全匹配。

天兵的意义
1.显然一个天兵可以在棋盘上随便走,在每次调换时,找一个棋子把它挪到目的地
   这相当于在二分图上任意填‘超能力次数’条边。
故先算出最大匹配数,再+超能力数,可以得到最优方案

模型建毕:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<iostream>
#include<functional>
#include<algorithm>
#include<queue>
using namespace std;
#define MAXN (100+10)
#define MAXM (100+10)
#define MAXK (100+10)
#define MAXT (100+1+10)
#define COST (bool(type)^(f[x][y]&1))?(h[x][y]<h[x+path[i][0]][y+path[i][1]]):(h[x][y]>h[x+path[i][0]][y+path[i][1]])
const int path[4][2]={{0,1},{1,0},{-1,0},{0,-1}};
int n,m,k,t,h[MAXM][MAXN],f[MAXM][MAXN];
queue< pair<int,int> > q;
bool inside(int x,int y)
{
	if (1<=x&&x<=m&&1<=y&&y<=n) {/*cout<<'A'<<x<<' '<<y<<endl;*/ return 1;}else return 0;
}
void bfs(bool type,int x,int y)  //type-> 0 upper 1 lower
{
	memset(f,127,sizeof(f));
	f[x][y]=0;
	q.push(make_pair(x,y));
	while (!q.empty())
	{
		pair<int ,int> now=q.front();
		int &x=now.first,&y=now.second;
		for (int i=0;i<4;i++)
		{
			int c=COST;
			if (inside(x+path[i][0],y+path[i][1]))
				if (f[x+path[i][0]][y+path[i][1]]>f[x][y]+c)
			{
		//		cout<<(x+path[i][0])<<' '<<(y+path[i][1])<<endl;
				f[x+path[i][0]][y+path[i][1]]=f[x][y]+c;	
				q.push(make_pair(x+path[i][0],y+path[i][1]));
			}
		}
		q.pop();
	}
}
pair<int,int> A[MAXT],B[MAXT];
int cost[MAXK][MAXT],a[MAXT];
bool b[MAXT];
bool find(int x,int maxcost)
{
	for (int i=1;i<=2*k+1;i++) 
		if (!b[i]&&cost[x][i]<=maxcost)
		{
			b[i]=1;
			if (a[i]==0) {a[i]=x;return 1;	}
			if (find(a[i],maxcost)) {a[i]=x; return 1;	}
		}	
	return 0;
}
int hopcroft(int maxcost)
{
	memset(a,0,sizeof(a));
	int ans=0;
	for (int i=1;i<=2*k;i++)
	{
		memset(b,0,sizeof(b));
		if (find(i,maxcost)) ans++;
	}	
//	cout<<ans<<endl;
	return ans;	
}
int b_search()
{
	int l=0,r=2*k;
	while (l<r)
	{
		m=(l+r)>>1;
		if (hopcroft(m)+m>=2*k) r=m;
		else l=m+1;
	}	
	return l;	
}
int main()
{
//	freopen("2547.in","r",stdin);
	scanf("%d%d%d%d",&m,&n,&k,&t);
	for (int i=1;i<=2*k+1;i++) scanf("%d%d",&A[i].first,&A[i].second);
	int size=1;
	for (int i=1;i<=t;i++)
	{
		int r;
		scanf("%d%d%d",&B[size].first,&B[size].second,&r);
		size++;
		for (r--;r;r--,size++) B[size]=B[size-1];
	}
	for (int i=1;i<=m;i++) 
		for (int j=1;j<=n;j++) 
			scanf("%d",&h[i][j]);
	
/*	bfs(1,1,2);
	for (int i=1;i<=m;i++)
	{
		for (int j=1;j<=n;j++) cout<<f[i][j]<<' ';
		cout<<'\n';
	}
*/	
	for (int i=1;i<=2*k;i++)
	{
		bfs((i>k),A[i].first,A[i].second);
		for (int j=1;j<=2*k+1;j++)
		{
			cost[i][j]=f[B[j].first][B[j].second];
//			cout<<i<<' '<<j<<' '<<cost[i][j]<<endl;
		}
	}
	
	cout<<b_search()<<endl;
	
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值