CQOI2006 / NKOJ2004--移动棋子

【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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值