1. 程式人生 > >[BZOJ 2547] 玩具兵

[BZOJ 2547] 玩具兵

.com lin CA air 個數 size match zoj sca

Link:

BZOJ 2547 傳送門

Solution:

很容易通過解可行性的單調性想到二分答案,接下來考慮如何驗證解

發現一個很奇妙的條件:步兵和騎兵的個數相同

因此交換位置時不用考慮可行性,保證能完成交換(口胡證明一下就行了)

於是可以將每一次交換位置想成轉變職業(不用考慮能否交換)

每一個士兵先用$bfs$預處理其到每個格子需要的轉變次數。

對於一次$check(mid)$,由於上述性質,$交換次數<=mid$的移動都是合法的

只要先跑一遍$交換次數<=mid$的士兵和位置的最大匹配$Max_{Match}$

再考慮$Max_{Match}+mid>=2*K$是否成立即可(多出的貢獻$mid$是對於“天兵”的特殊處理)

Code:

#include <bits/stdc++.h>

using namespace std;
typedef pair<int,int> P;
#define X first
#define Y second
const int MAXN=1e3+10;
int dx[]={0,0,1,-1},dy[]={1,-1,0,0};
P dat[MAXN],ly[MAXN];
int N,M,K,T,h[MAXN][MAXN],tot=0;
int match[MAXN],dist[MAXN][MAXN],w[MAXN][MAXN];
bool vis[MAXN];

void bfs(int st_x,int st_y,int f) { queue<P> que;que.push(P(st_x,st_y)); memset(dist,0x3f,sizeof(dist));dist[st_x][st_y]=0; while(!que.empty()) { P t=que.front();que.pop(); for(int i=0;i<4;i++) { int fx=t.X+dx[i],fy=t.Y+dy[i],delta;
if(fx<1 || fx>N || fy<1 || fy>M) continue; if(f^(dist[t.X][t.Y]&1)) if(h[fx][fy]<=h[t.X][t.Y]) delta=0; else delta=1; else if(h[fx][fy]>=h[t.X][t.Y]) delta=0; else delta=1; if(dist[fx][fy]>dist[t.X][t.Y]+delta) { dist[fx][fy]=dist[t.X][t.Y]+delta; que.push(P(fx,fy)); } } } } bool dfs(int u,int lim) { for(int i=1;i<=tot;i++) if(!vis[i] && w[u][i]<=lim) { vis[i]=true; if(match[i]==-1 || dfs(match[i],lim)) { match[i]=u; return true; } } return false; } bool check(int x) { memset(match,-1,sizeof(match)); int ret=0; for(int i=1;i<=2*K;i++) { memset(vis,0,sizeof(vis)); if(dfs(i,x)) ret++; } return (ret+x)>=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",&dat[i].X,&dat[i].Y); for(int i=1;i<=T;i++) { int x,y,z;scanf("%d%d%d",&x,&y,&z); while(z--) ly[++tot]=P(x,y); } 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++) //bfs預處理 { if(i<=K) bfs(dat[i].X,dat[i].Y,0); else bfs(dat[i].X,dat[i].Y,1); for(int j=1;j<=tot;j++) w[i][j]=dist[ly[j].X][ly[j].Y]; } int l=0,r=K*2; //二分答案 while(l<=r) { int mid=(l+r)>>1; if(check(mid)) r=mid-1; else l=mid+1; } printf("%d",l); return 0; }

Review:

此題的難點在於將 交換位置$->$轉變職業

還是要註意題目中的特殊性質(EX:兩職業個數相同),看看能不能推出一些奇妙的結論

[BZOJ 2547] 玩具兵