【CQOI2006】移动棋子
Time Limit:50000MS Memory Limit:65536K
Case Time Limit:5000MS
Description
在一个n*n的棋盘上有n枚棋子。每次可以把一枚棋子往上、下、左、右方向之一移动一格,最后排成一行、一列或者主、副对角线上(因此一共有2n+2条可能的目标状态),要求移动次数最小。
棋盘上有一些位置是障碍,棋子在任何时候都不能经过。棋子的初始位置保证不在障碍物上。任两枚棋子不能在同时到达 同一个格子。
Input
第一行包含两个整数n, m,表示棋子的个数(它也是棋盘的边长)和障碍的个数。以下n行,每行两个整数(x, y),表示第i个棋子的坐标(1<=x, y<=n),以下m行,每行给出一个障碍物的坐标。假设这n+m个坐标两两不重合。
Output
一个整数,表示最小移动步数。如果无解,输出-1。
Sample Input
5 1
1 2
2 4
3 4
5 1
5 3
1 1
Sample Output
6
Hint
50%的数据满足:2<=n<=15,m=0
100%的数据满足:2<=n<=50, 0<=m<=100
分析:不需要考虑每个棋子的移动情况,只管最终状态(2n+2种),于是最小费用最大流。
建图:
S向每个棋子连边,FLOW = 1,COST = 0
每个棋子向每个最终位置连边,FLOW = INF,COST = bfs
每个最终位置向T连边,FLOW = 1,COST = 0
预处理出每个棋子到棋盘上的每个位置所需的最小步数,这里采用的是bfs
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<queue>
using namespace std;
const int inf=1000000000;
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};
int n,m;
int N;
int maxflow;
int mincost;
int tot_edge=0;
int flow,cost;
int dis[10005],path[10005];
int a[10005];
bool flag[10005];
bool obstacle[1005][1005];
int map[55][55][55];
bool vis[55][55];
struct line{
int from,to,len,remain,fan;
};
line edge[100005];
int last[10005],_next[100005];
void clean(){
tot_edge=0;
maxflow=0;
mincost=0;
memset(dis,0,sizeof(dis));
memset(path,0,sizeof(path));
memset(flag,0,sizeof(flag));
memset(edge,0,sizeof(edge));
memset(last,0,sizeof(last));
memset(_next,0,sizeof(_next));
}
void add_edge(int x,int y,int l,int acc){
tot_edge++;
edge[tot_edge].from=x;
edge[tot_edge].to=y;
edge[tot_edge].len=l;
edge[tot_edge].remain=acc;
edge[tot_edge].fan=tot_edge+1;
_next[tot_edge]=last[x];
last[x]=tot_edge;
tot_edge++;
edge[tot_edge].from=y;
edge[tot_edge].to=x;
edge[tot_edge].len=-l;
edge[tot_edge].remain=0;
edge[tot_edge].fan=tot_edge-1;
_next[tot_edge]=last[y];
last[y]=tot_edge;
}
bool findpath(){
queue<int> q;
int i,j,now;
for(i=1;i<=N+2;i++){
dis[i]=inf;
path[i]=-1;
flag[i]=false;
}
dis[N+1]=path[N+1]=0;
flag[N+1]=true;
q.push(N+1);
while(q.size()){
j=q.front();
q.pop();
flag[j]=false;
for(int h=last[j];h;h=_next[h]){
int v=edge[h].to;
if(edge[h].remain&&(dis[v]>dis[j]+edge[h].len)){
dis[v]=dis[j]+edge[h].len;
path[v]=h;
if(flag[v]==false){
flag[v]=true;
q.push(v);
}
}
}
}
if(dis[N+2]<inf)return true;
else return false;
}
void addflow(){
int flow,cost,i;
flow=inf;cost=0;
i=N+2;
while((i!=N+1)&&(path[i]!=0)){
flow=min(flow,edge[path[i]].remain);
cost+=edge[path[i]].len;
i=edge[path[i]].from;
}
maxflow+=flow;
mincost+=flow*cost;
i=N+2;
while(i!=N+1){
edge[path[i]].remain-=flow;
edge[edge[path[i]].fan].remain+=flow;
i=edge[path[i]].from;
}
}
struct node{
int x,y;
node(){}
node(int a,int b){x=a;y=b;}
};
node point[55];
int main(){
int i,j,k;
int ans=inf;
cin>>n>>m;
N=2*n;
for(i=1;i<=n;i++){
scanf("%d%d",&point[i].x,&point[i].y);
}
for(i=1;i<=m;i++){
int x,y;
cin>>x>>y;
obstacle[x][y]=true;
}
for(i=1;i<=n;i++){
memset(vis,0,sizeof(vis));
queue<node> q;
while(q.size())q.pop();
q.push(point[i]);
vis[point[i].x][point[i].y]=true;
while(q.size()){
node head=q.front();
q.pop();
for(int h=0;h<4;h++){
int tx=head.x+dx[h];
int ty=head.y+dy[h];
if(tx<=n&&tx>=1&&ty>=1&&ty<=n&&vis[tx][ty]==false&&obstacle[tx][ty]==false){
vis[tx][ty]=true;
map[i][tx][ty]=map[i][head.x][head.y]+1;
q.push(node(tx,ty));
}
}
}
for(j=1;j<=n;j++){
for(k=1;k<=n;k++){
if(map[i][j][k]==0)map[i][j][k]=inf;
}
}
map[i][point[i].x][point[i].y]=0;
/*cout<<"fuck:"<<i<<"over"<<endl;
for(j=1;j<=n;j++){
for(k=1;k<=n;k++){
if(map[i][j][k]==inf)cout<<"inf"<<" ";
else cout<<map[i][j][k]<<" ";
}
cout<<endl;
}*/
}
for(i=1;i<=n;i++){
clean();
for(j=1;j<=n;j++){
add_edge(N+1,j,0,1);
add_edge(j+n,N+2,0,1);
}
for(j=1;j<=n;j++){
for(k=1;k<=n;k++){
add_edge(j,n+k,map[j][i][k],inf);
}
}
maxflow=0;
mincost=0;
while(findpath())addflow();
if(maxflow==n)ans=min(ans,mincost);
}
//cout<<"fuck:heng over!"<<endl;
for(i=1;i<=n;i++){
clean();
for(j=1;j<=n;j++){
add_edge(N+1,j,0,1);
add_edge(j+n,N+2,0,1);
}
for(j=1;j<=n;j++){
for(k=1;k<=n;k++){
add_edge(j,n+k,map[j][k][i],inf);
}
}
maxflow=0;
mincost=0;
while(findpath())addflow();
if(maxflow==n)ans=min(ans,mincost);
}
//cout<<"fuck:shu over!"<<endl;
clean();
for(j=1;j<=n;j++){
add_edge(N+1,j,0,1);
add_edge(j+n,N+2,0,1);
}
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
add_edge(i,j+n,map[i][j][j],inf);
}
}
maxflow=0;
mincost=0;
while(findpath())addflow();
if(maxflow==n)ans=min(ans,mincost);
clean();
for(j=1;j<=n;j++){
add_edge(N+1,j,0,1);
add_edge(j+n,N+2,0,1);
}
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
add_edge(i,j+n,map[i][j][n-j+1],inf);
}
}
maxflow=0;
mincost=0;
while(findpath())addflow();
if(maxflow==n)ans=min(ans,mincost);
if(ans>=inf){
puts("-1");
return 0;
}
cout<<ans;
}