[SDOI2012]象棋

題目

展開
題目描述
小云和小南兩姐妹從小喜歡下象棋,現在作爲象棋高手的她們,已經不屑於玩平常的象棋了,於是她們便開始用棋盤和棋子玩各種各樣的新遊戲。

今天天氣晴朗,陽光明媚,她們將在n * m的棋盤上進行遊戲。

棋盤上有k顆棋子和若干有障礙格子,令棋盤左上角格子座標爲(1, 1),右下角格子座標爲(n, m),參數a、b規定了所有棋子的走法:在(x, y)的棋子下一步能走到(x + a, y + b), (x + a, y - b), (x – a, y + b), (x – a, y – b), (x + b, y + a), (x + b, y - a), (x – b, y + a), (x – b, y – a)這八個格子中的一個,棋子任何時候不能躍出棋盤或走到有障礙的格子上。

這k顆棋子是相同的,小云和小南的目標是用最少步數把所有棋子移動到特定格子,要求移動過程中不能出現多顆棋子同時在某一格的情況。

她們已經想出步數較少方案,但無法確定這是否爲最少步數,所以向作爲程序員的你求助。

輸入格式
第一行五個空格隔開的整數n、m、k、a以及b;

接下來n行,每行爲長度m的字符串,描述棋盤,‘.’表示沒有障礙的格子,‘*’表示有障礙的格子;

接下來k行,每行兩個整數x和y,分別表示k顆棋子的初始位置;

接下來k行,每行兩個整數x和y,分別表示k顆棋子的目標位置。

輸出格式
一個整數,爲把所有棋子移動到’t’位置的最少步數,數據保證有解。

輸入輸出樣例
輸入 #1複製
1 8 2 2 0
…*
1 1
1 3
1 5
1 7
輸出 #1複製
4
說明/提示
樣例說明

一可行方案如下:第二顆棋子向右跳兩步,隨後第一顆棋子向右跳兩步,共4步。值得注意的是,第一顆棋子向右跳三步,隨後第二顆棋子向右跳一步的方案儘管能把棋子都移動到目標位置,但途中兩顆棋子曾經同時在(1, 3),違反了規則,所以不能選用此方案。

數據範圍

其中20%的數據,n * m ≤ 20;

另外10%的數據,n = 1;

對於100%的數據,n、m ≤ 100,k ≤ 500。

思路

首先,“不能出現多顆棋子同時在某一格的情況”條件沒有用,可以通過調整路徑順序避免這種情況。

考慮預處理所有起始點和目標點的距離,用BFS實現,應該都會吧。之後就是裸的二分圖最小權匹配了,求出所有起始點到所有終點的最小權匹配,將邊權取相反數直接跑KM即可,此處用BFS版KM保證時間複雜度。

代碼

#include<bits/stdc++.h>
using namespace std;
const int N=109,M=509; 
char mp[N][N]; 
int d[N][N],qx[N*N],qy[N*N],n,m,k,a,b,sx[M],sy[M],tx[M],ty[M],lx[M],ly[M],sl[M],w[M][M],mt[M],p[M]; 
bool f[M]; 
int main(){
	register int i,j,l,x,y,u,v,h,t; 
	scanf("%d%d%d%d%d",&n,&m,&k,&a,&b),memset(lx,-9,sizeof(lx)); 
	register int nx[9]={a,a,-a,-a,b,b,-b,-b},ny[9]={b,-b,b,-b,a,-a,a,-a}; 
	for(i=1; i<=n; ++i)scanf("%s",mp[i]+1); 
	for(i=1; i<=k; ++i)scanf("%d%d",sx+i,sy+i); 
	for(i=1; i<=k; ++i)scanf("%d%d",tx+i,ty+i); 
	for(i=1; i<=k; ++i){
		h=0,t=1,qx[1]=sx[i],qy[1]=sy[i],memset(d,9,sizeof(d)),d[sx[i]][sy[i]]=0; 
		while(h!=t){
			u=qx[++h],v=qy[h]; 
			for(j=0; j<8; ++j){
				x=u+nx[j],y=v+ny[j]; 
				if(x>0&&y>0&&x<=n&&y<=m&&mp[x][y]=='.'&&d[x][y]>1e8)d[x][y]=d[u][v]+1,qx[++t]=x,qy[t]=y; 
			}
		}
		for(j=1; j<=k; ++j)w[i][j]=-d[tx[j]][ty[j]],lx[i]=lx[i]>w[i][j]?lx[i]:w[i][j]; 
	}
	for(i=1; i<=k; ++i){
		memset(sl,9,sizeof(sl)),memset(f,0,sizeof(f)); 
		for(mt[j=0]=i; mt[j]; j=u){
			f[j]=1,l=1e8,x=mt[j]; 
			for(y=1; y<=k; ++y)if(!f[y]){
				v=lx[x]+ly[y]-w[x][y]; 
				if(v<sl[y])sl[y]=v,p[y]=j; 
				if(l>sl[y])l=sl[y],u=y; 
			}
			for(y=0; y<=k; ++y)if(f[y])lx[mt[y]]-=l,ly[y]+=l; else sl[y]-=l; 
		}
		while(j)mt[j]=mt[p[j]],j=p[j]; 
	}//KM最小權匹配
	for(l=0,i=1; i<=k; ++i)l-=lx[i]+ly[i]; 
	printf("%d",l); 
	return 0; 
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章