【CQOI 2006】移动棋子

来源:互联网 发布:大学生网络信贷案例 编辑:程序博客网 时间:2024/05/17 22:56

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 11 22 43 45 15 31 1

Sample Output

6

Hint

50%的数据满足:2<=n<=15,m=0 
100%的数据满足:2<=n<=50, 0<=m<=100


【分析】

枚举+费用流

每次枚举一行或一列或一条对角线,判断这一行、列、对角线有没有障碍,如果没有,那么建图:
(1)源点→棋子初始位置,流量1,费用0;
(2)棋子的初始位置→任意一个目标位置,流量1,费用为移动的步数(可以预处理出来);
(3)目标位置→汇点,流量1费用0。
然后费用流跑一下,记下费用最小的一次就可以了。
不需要什么优化,除了费用流以外,此题纯属考察代码能力。
每次费用流O(N^3),加上枚举了2N+2次,总的复杂度O(N^4),一秒钟就够了但是这道题有5秒钟。


【代码】

/***************************    ID:CiocioLANG:C++DATE:2013-12-22TAKS:CQOI 2006 Move***************************/#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iostream>#include <algorithm>#include <queue>using namespace std;#define MAXR 55#define MAXN 5005#define INF 999999999#define loc(x,y) ((x-1)*N+y)#define MAXM 50000int N,M,Sta,End,Maxflow,Mincost,ans=INF;int mat[MAXR][MAXR],step[MAXR][MAXN];int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};bool vis[MAXR][MAXR],row[MAXR],cow[MAXR],ma,mt;int Dis[MAXN],path[MAXN],mark[MAXN],chose[MAXN];struct wjj{int x,y;}A[MAXR];int Y[MAXM],W[MAXM],C[MAXM],Next[MAXM],Last[MAXN];int totE=1;void _addedge(int u,int v,int w,int c){Y[++totE]=v;W[totE]=w;C[totE]=c;Next[totE]=Last[u];Last[u]=totE;Y[++totE]=u;W[totE]=0;C[totE]=-c;Next[totE]=Last[v];Last[v]=totE;}void _init(){scanf("%d%d",&N,&M);int x,y;for(int i=1;i<=N;i++){scanf("%d%d",&x,&y);mat[x][y]=1;A[i].x=x;A[i].y=y;}for(int i=1;i<=M;i++){scanf("%d%d",&x,&y);mat[x][y]=-1;row[y]=true;cow[x]=true;if(x==y) ma=true;if(x+y==N+1) mt=true;}}struct node{int x,y;node(int _x,int _y):x(_x),y(_y){}};queue <node> BQ;void _Bfs(int u){memset(vis,0,sizeof(vis));while(!BQ.empty()) BQ.pop();int i=A[u].x,j=A[u].y;BQ.push(node(i,j));vis[i][j]=true;while(!BQ.empty()){node p=BQ.front();BQ.pop();int x=p.x,y=p.y;for(int k=0;k<=3;k++){int tx=x+dir[k][0],ty=y+dir[k][1];if(1<=tx&&tx<=N&&1<=ty&&ty<=N&&mat[tx][ty]!=-1&&vis[tx][ty]==false){step[u][loc(tx,ty)]=step[u][loc(x,y)]+1;vis[tx][ty]=true;BQ.push(node(tx,ty));}}}}void _addflow(){int flow,i,cost;cost=0;flow=INF;i=End;while((i!=0)&&(path[i]!=-1)){flow=min(flow,W[chose[i]]);cost+=C[chose[i]];i=path[i];}Maxflow+=flow;Mincost+=flow*cost;i=End;while((i!=0)&&(path[i]!=-1)){W[chose[i]]-=flow;W[chose[i]^1]+=flow;i=path[i];}}queue <int> Q;bool _findpath(){for(int i=Sta;i<=End;i++) {Dis[i]=INF;path[i]=-1;mark[i]=false;}while(!Q.empty()) Q.pop();Dis[Sta]=0;path[Sta]=-1;mark[Sta]=true;Q.push(Sta);while(!Q.empty()){int u=Q.front();Q.pop();mark[u]=false;for(int j=Last[u];j;j=Next[j]){int v=Y[j];if(W[j]>0&&Dis[v]>Dis[u]+C[j]){Dis[v]=Dis[u]+C[j];path[v]=u;chose[v]=j;if(!mark[v]){mark[v]=true;Q.push(v);}}}}return Dis[End]<INF;}void _work(){for(int i=1;i<=N;i++) _addedge(Sta,i,1,0);while(_findpath()) _addflow();totE=1;memset(Last,0,sizeof(Last));ans=min(ans,Mincost);Mincost=Maxflow=0;}void _solve(){for(int i=1;i<=N;i++) _Bfs(i);Sta=0;End=N+N*N+1;for(int j=1;j<=N;j++)          //行if(!row[j]){for(int i=1;i<=N;i++){int u=loc(i,j);_addedge(u+N,End,1,0);for(int k=1;k<=N;k++) _addedge(k,u+N,1,step[k][u]);}    _work();}for(int i=1;i<=N;i++)          //列if(!cow[i]){for(int j=1;j<=N;j++){int u=loc(i,j);_addedge(u+N,End,1,0);for(int k=1;k<=N;k++) _addedge(k,u+N,1,step[k][u]);}_work();}if(!ma)                      //两条对角线{for(int i=1;i<=N;i++){int u=loc(i,i);_addedge(u+N,End,1,0);for(int k=1;k<=N;k++) _addedge(k,u+N,1,step[k][u]);}_work();}if(!mt){for(int i=1;i<=N;i++){int u=loc(i,N+1-i);_addedge(u+N,End,1,0);for(int k=1;k<=N;k++) _addedge(k,u+N,1,step[k][u]);}_work();}ans=ans==INF?-1:ans;cout<<ans<<endl;}int main(){_init();_solve();return 0;}


0 0