题解 P7198 [CTSC2002] 玩具兵
感觉这题思路比较像 P2402,但是稍微复杂一些。
做法
首先我们假设没有天兵的情况。
不难想到,超能力是在每个兵都不能移动时使用,这样显然不劣。
其次,一定是骑兵与步兵互相交换。
于是可以先预处理出对于每个兵 $K_i$ 到目标格子 $T_j$,记为 $dis_{i,j}$。
然后,再二分超能力的使用次数,记为 $mid$。
这里进行建模:
-
源点向每个兵建一条边,容量为 $1$。
-
每个目标格子向汇点建一条边,容量为 $R_i$。
-
$\forall dis_{i,j}\le mid$,兵 $K_i$ 向 目标格子 $T_j$,建一条边,容量为 $1$。
在上图中跑最大流,记为 $cnt$,当 $cnt\ge2\times K$ 答案既合法。
这时再来讨论有天兵的情况。
首先天兵可以干什么?可以在使用超能力的时候把兵放到目标格子去。可以放多少个?可以放 $mid$ 个。所以最后的判断改成 $mid+cnt\ge2\times K$ 即可。
实现
稍微说一下预处理 $dis$。
我是用的 SPFA,但是据说可以用 01bfs。
首先是对每个点都跑一边 SPFA。
对于当前到底是骑兵还是步兵可以用当前步数的奇偶性来判断。
代码
#include<cstring>
#include<iostream>
#include<queue>
#define pii pair<int,int>
#define mp(x,y) make_pair(x,y)
using namespace std;
struct sss
{
int q,w,e,nxt;
}a[1000005];
int cnt,head[1000005],d[1000005],n,now[1000005],m,N,S,T;
using namespace std;
queue<int>q;
void adde(int q,int w,int e)
{
a[++cnt].q=q;
a[cnt].w=w;
a[cnt].e=e;
a[cnt].nxt=head[q];
head[q]=cnt;
a[++cnt].q=w;
a[cnt].w=q;
a[cnt].e=0;
a[cnt].nxt=head[w];
head[w]=cnt;
}
bool bfs()
{
memset(d,0,sizeof(d));
while(q.size())q.pop();
q.push(S);
d[S]=1;
now[S]=head[S];
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=head[x];i;i=a[i].nxt)
{
if(a[i].e&&!d[a[i].w])
{
q.push(a[i].w);
now[a[i].w]=head[a[i].w];
d[a[i].w]=d[x]+1;
if(a[i].w==T)return 1;
}
}
}
return 0;
}
int dinic(int x,int flow)
{
if(x==T)return flow;
int rest=flow;
for(int i=now[x];i&&rest;i=a[i].nxt)
{
now[x]=i;
if(a[i].e&&d[a[i].w]==d[x]+1)
{
int k=dinic(a[i].w,min(rest,a[i].e));
if(!k)d[a[i].w]=0;
a[i].e-=k;
a[i%2?i+1:i-1].e+=k;
rest-=k;
}
}
return flow-rest;
}
// Dinci //
int dis[105][105],vis[105][105],h[105][105],t,K;
int dx[]={0,1,0,-1};
int dy[]={1,0,-1,0};
bool check(pii x)
{
if(x.first<1||x.first>n||x.second<1||x.second>m)return 0;
return 1;
}
void SPFA(int sx,int sy,int d)
{
queue<pair<int,int>>q;
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
vis[sx][sy]=1;
dis[sx][sy]=0;
q.push(mp(sx,sy));
while(!q.empty())
{
pii x=q.front();
q.pop();
vis[x.first][x.second]=0;
for(int i=0;i<4;i++)
{
pii y=mp(x.first+dx[i],x.second+dy[i]);
if(!check(y))continue;
int w=0,p=(dis[x.first][x.second]&1)^d;//0b1q
if(h[y.first][y.second]<h[x.first][x.second]&&!p)w=1;
if(h[y.first][y.second]>h[x.first][x.second]&&p)w=1;
if(dis[y.first][y.second]>dis[x.first][x.second]+w)
{
dis[y.first][y.second]=dis[x.first][x.second]+w;
if(!vis[y.first][y.second])q.push(y),vis[y.first][y.second]=1;
}
}
}
}
// SPFA //
int pos[10005][2],tar[10005][3],Dis[1005][1005];
bool check(int mid)
{
cnt=0;
memset(head,0,sizeof(head));
for(int i=1;i<=2*K;i++)adde(S,i,1);
for(int i=1;i<=t;i++)adde(i+2*K,T,tar[i][2]);
for(int i=1;i<=2*K;i++)
{
for(int j=1;j<=t;j++)
{
if(Dis[i][j]<=mid)adde(i,j+2*K,1);
}
}
int ans=0,flow=0;
while(bfs())while(flow=dinic(S,1e9))ans+=flow;
return ans+mid>=2*K;
}
int main()
{
scanf("%d%d%d%d",&n,&m,&K,&t);
for(int i=1;i<=2*K+1;i++)scanf("%d%d",&pos[i][0],&pos[i][1]);
for(int i=1;i<=t;i++)scanf("%d%d%d",&tar[i][0],&tar[i][1],&tar[i][2]);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)scanf("%d",&h[i][j]);
}
for(int i=1;i<=2*K;i++)
{
SPFA(pos[i][0],pos[i][1],i>K);
for(int j=1;j<=t;j++)Dis[i][j]=dis[tar[j][0]][tar[j][1]];
}
S=2*K+t+1;
T=2*K+t+2;
int l=0,r=2*K,ans;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid))
{
r=mid-1;
ans=mid;
}
else l=mid+1;
}
printf("%d",ans);
}