题解 [CTSC2002]玩具兵

本文介绍了一种结合最短路径算法与二分图最大匹配算法解决特定问题的方法。该问题涉及兵种移动与超能力使用的策略优化,通过计算不同位置间的最短路径,并利用最大匹配算法来确定最优的兵种调度方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这个题我们把三个单位分开讨论

骑兵和步兵:由于一次超能力可以随便换任意次,容易想到对于一个步兵来说走到第 iii 个点其使用超能力的次数应该是一个最短路问题,从 (x1,y1)(x_1,y_1)(x1,y1) 走到 (x2,y2)(x_2,y_2)(x2,y2)hx1,y1<hx2,y2h_{x_1,y_1}<h_{x_2,y_2}hx1,y1<hx2,y2 则边权为 111(随便找个骑兵超能力一下),否则边权为 000。骑兵同理。

天兵:妥妥的工具人,由于对非超能力移动不做限制,天兵可以满图乱跑,跑到目标点再超能力一下把一个步兵或骑兵换过来,然后去下一个点。

然后就是,由于要求套路的二分答案超能力的次数,假如说第 iii 个兵到 第 jjj 个目标点所用超能力次数大于二分的这个值就不用管(你也管不了不是),那么剩下的这些信息就是可以到的了,我们希望在不使用天兵的情况下尽可能多的让其它兵种上点,显然的我们跑一次二分图最大匹配即可(注意:一个目标点最多可以有 rir_iri 个兵在那里,遇到这种无法用常规方法处理的东西要拆点,我们把一个目标点拆成 rir_iri 个目标点再跑最大匹配,最短路的时候需要注意是跑原先的图,再用到第 iii 个点的最短路去更新这拆出的 rir_iri 个点)。

然后剩下这些没匹配上的兵,我们只能用天兵去传送,这个时候点的限制就无意义了,天兵可以随便飞。这样我们使用超能力的次数为 2k−ans2k-ans2kansansansans 表示最大匹配数),再把这个值跟 midmidmid 比较即可。

代码(比较丑,仅供对拍)

#include<bits/stdc++.h>
using namespace std;
int n,m,k,t,s,vis[2005][2005],dis[2005][2005],h[505][505],d[2005][2005],tot;
int ssh(int x,int y){
	return (x-1)*m+y;
}
vector<int>G[200];
struct troop{
	int num,x,y;
}tp[206],re[206],R[206];
struct node{
	int x,y;
};  
struct qqq{
	int y,next;
	int used;
}a[5001],fc[500];
const int dx[]={-1,1,0,0};
const int dy[]={0,0,1,-1};
void BFS(int sx,int sy,bool M){
	queue<node>q;
	memset(vis,0,sizeof(vis));
	memset(dis,0x3f,sizeof(dis));
	dis[sx][sy]=0;
	q.push((node){sx,sy});
	while(!q.empty()){
		node top=q.front();
		q.pop();
		vis[top.x][top.y]=0;
		for(int i=0;i<4;i++){
			int ex=top.x+dx[i];
			int ey=top.y+dy[i];
			if(ex<1||ex>n||ey<1||ey>m)
			continue;
			int ds=0;
			int tmp=((dis[top.x][top.y]&1)^M);
			if(h[ex][ey]>h[top.x][top.y]&&tmp){
				ds=1;
			}
			if((h[ex][ey]<h[top.x][top.y]&&!tmp)){
				ds=1;
			}
			if(dis[top.x][top.y]+ds<dis[ex][ey]){
				dis[ex][ey]=dis[top.x][top.y]+ds;
				if(!vis[ex][ey]){ 
					q.push((node){ex,ey});
					vis[ex][ey]=1;
				}
			}
		}
	}
}
int match[200],vs[200];
int dfs(int u){
	for(int i=0;i<G[u].size();i++){
		int y=G[u][i];
		if(!vs[y]){
			vs[y]=1;
			if(!match[y]||dfs(match[y])){
				match[y]=u;
				return 1;				
			}
		}
	}
	return 0;
}
bool check(int mid){
	for(int i=0;i<=2*k;i++){
		G[i].clear();
	}
	memset(match,0,sizeof(match));
	for(int i=1;i<=2*k;i++){
		for(int j=1;j<=tot;j++){
			if(d[i][j]<=mid){
				//add(i,j);
				G[i].push_back(j);
			} 
		}
	}
	int ans=0;
	for(int i=1;i<=2*k;i++){
		memset(vs,0,sizeof(vs));
		ans+=dfs(i);
	}
	return ans+mid>=2*k;
	
}
int main()
{
	ios::sync_with_stdio(0);
	cin>>n>>m>>k>>t;
	tot=0;
	for(int i=1;i<=2*k+1;i++){
		cin>>tp[i].x>>tp[i].y;
	}
	for(int i=1;i<=t;i++){
		cin>>re[i].x>>re[i].y>>re[i].num;
		while(re[i].num--){
			R[++tot].x=re[i].x;
			R[tot].y=re[i].y;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>h[i][j];
		}
	}
	for(int i=1;i<=2*k;i++){
		BFS(tp[i].x,tp[i].y,i>k);
		for(int j=1;j<=tot;j++){
			d[i][j]=dis[R[j].x][R[j].y];
		}	
	}
	int L=0,rR=2*k;
	while(L<rR){
		int mid=(L+rR)/2;
		if(check(mid))
		rR=mid;
		else
		L=mid+1;
	} 
	cout<<L<<endl;
   return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值