[Bzoj 2547] [Ctsc2002] 玩具兵
2547: [Ctsc2002]玩具兵
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 317 Solved: 152
[Submit][Status][Discuss]
Description
小明的爸爸給他買了一盒玩具兵,其中有 K個步兵,K個騎兵和一個天兵,個個高大威猛,形象逼真。盒子裏還有一個M*N棋盤,每個格子(i,j)都有一個高度Hij,並且大得足以容納所有的玩具兵。小明把所有的玩具兵都放到棋盤上去,突然想到了一種很有趣的玩法:任意挑選T個不同的格子,並給每個格子i規定一個重要值Ri--,遊戲的目標就是每次沿東南西北之一的方向把一個玩具兵移動到其相鄰的格子中(但不能移動到棋盤外面去),最終使得每個挑選出的格子i上恰好有Ri
- 步兵只會往高處爬,因此如果兩個格子A和B相鄰,當且僅當格子A的高度小於或等於B,步兵才可以從A移動到B。
- 騎兵只會往低處跳,因此如果兩個格子A和B相鄰,當且僅當格子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。
Solution
觀察到每次交換可以換無數次,所以每次一定是把步兵和騎兵換一下,相當於每個都走到無路可走的地方,然後換個職業繼續走。
所以對於每個玩具兵(先不考慮天兵),都處理出到每個點最少要交換的次數,也就是說,至少要交換 _dis[i][j] 次才能到達 (i,j)。
然後二分答案,二分出交換的次數 mid,然後對於每個玩具兵,向交換次數小於 mid 的終點連邊,然後跑最大流即可。
註意最後要加上二分出的 mid,意義就是天兵每次都可以替代一個玩具兵走到終點,然後換它們倆就好。
Code
// By YoungNeal #include<queue> #include<cstdio> #include<cstring> int d[1000005]; int s,t,maxflow; int head[1000005]; int toy[1005][10]; int goal[1005][10]; int dis[1005][105]; // 記錄交換了幾次 dis[i][j]->第i個兵到第j個目標 int m,n,k,T,ans,cnt; int height[105][105]; int _dis[105][105][10]; // _dis[i][j]->從當前的點到(i,j)最少交換次數 bool in[105][105][5]; // in[i][j][p]->當前點到(i,j)職業為p是否在隊列 int dx[]={-1,0,1,0},dy[]={0,1,0,-1}; struct Node{ int x,y,o; }; struct Edge{ int to,nxt,flow; }edge[10000005]; void add(int x,int y,int z){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; edge[cnt].flow=z; head[x]=cnt; } void spfa(int now,int xx,int yy,int cnt){ std::queue<Node> q; memset(in,0,sizeof in); memset(_dis,0x3f,sizeof _dis); _dis[xx][yy][cnt]=0; q.push((Node){xx,yy,cnt}); while(q.size()){ int x=q.front().x; int y=q.front().y; int o=q.front().o; q.pop(); in[x][y][o]=0; for(int k=0;k<4;k++){ int nx=x+dx[k]; int ny=y+dy[k]; int nowdis,oo; if(nx<1||nx>m||ny<1||ny>n) continue; if((o==0&&height[nx][ny]>=height[x][y])||(o==1&&height[nx][ny]<=height[x][y])) nowdis=_dis[x][y][o],oo=o; else nowdis=_dis[x][y][o]+1,oo=o^1; if(_dis[nx][ny][oo]>nowdis){ _dis[nx][ny][oo]=nowdis; if(!in[nx][ny][oo]) in[nx][ny][oo]=1,q.push((Node){nx,ny,oo}); } } } for(int i=1;i<=T;i++){ dis[now][i]=std::min(_dis[goal[i][1]][goal[i][2]][0],_dis[goal[i][1]][goal[i][2]][1]); } } void clear(){ maxflow=0; cnt=1;s=k*2+T+1;t=s+1; memset(edge,0,sizeof edge); memset(head,0,sizeof head); } bool bfs(){ std::queue<int> q; memset(d,0,sizeof d); q.push(s);d[s]=1; while(q.size()){ int u=q.front();q.pop(); for(int i=head[u];i;i=edge[i].nxt){ int to=edge[i].to; if(d[to]) continue; if(!edge[i].flow) continue; d[to]=d[u]+1; q.push(to); if(to==t) return 1; } } return 0; } int dinic(int now,int flow){ if(now==t) return flow; int rest=flow,k; for(int i=head[now];i;i=edge[i].nxt){ if(!rest) return flow; int to=edge[i].to; if(!edge[i].flow) continue; if(d[to]!=d[now]+1) continue; k=dinic(to,std::min(rest,edge[i].flow)); if(!k) d[to]=0; edge[i].flow-=k; edge[i^1].flow+=k; rest-=k; } return flow-rest; } bool check(int mid){ clear(); for(int i=1;i<=k*2;i++){ for(int j=1;j<=T;j++){ if(dis[i][j]<=mid) add(i,j+k*2,1),add(j+k*2,i,0); } } for(int i=1;i<=k*2;i++) add(s,i,1),add(i,s,0); for(int i=1;i<=T;i++) add(i+k*2,t,goal[i][3]),add(t,i+k*2,0); int flow=0; while(bfs()) while(flow=dinic(s,0x3f3f3f3f)) maxflow+=flow; return maxflow+mid>=k*2; } signed main(){ scanf("%d%d%d%d",&m,&n,&k,&T); for(int i=1;i<=k*2+1;i++) scanf("%d%d",&toy[i][1],&toy[i][2]); for(int i=1;i<=T;i++) scanf("%d%d%d",&goal[i][1],&goal[i][2],&goal[i][3]); for(int i=1;i<=m;i++){ for(int j=1;j<=n;j++) scanf("%d",&height[i][j]); } for(int i=1;i<=(k<<1);i++) spfa(i,toy[i][1],toy[i][2],(i>k)); int l=0,r=(k<<1)+1; while(l<=r){ int mid=l+r>>1; if(check(mid)) ans=mid,r=mid-1; else l=mid+1; } printf("%d\n",ans); return 0; }
[Bzoj 2547] [Ctsc2002] 玩具兵