【題解】luogu P3956 棋盤
題目描述
有一個m×m的棋盤,棋盤上每一個格子可能是紅色、黃色或沒有任何顏色的。你現在要從棋盤的最左上角走到棋盤的最右下角。
任何一個時刻,你所站在的位置必須是有顏色的(不能是無色的), 你只能向上、 下、左、 右四個方向前進。當你從一個格子走向另一個格子時,如果兩個格子的顏色相同,那你不需要花費金幣;如果不同,則你需要花費 1個金幣。
另外, 你可以花費 2 個金幣施展魔法讓下一個無色格子暫時變為你指定的顏色。但這個魔法不能連續使用, 而且這個魔法的持續時間很短,也就是說,如果你使用了這個魔法,走到了這個暫時有顏色的格子上,你就不能繼續使用魔法; 只有當你離開這個位置,走到一個本來就有顏色的格子上的時候,你才能繼續使用這個魔法,而當你離開了這個位置(施展魔法使得變為有顏色的格子)時,這個格子恢復為無色。
現在你要從棋盤的最左上角,走到棋盤的最右下角,求花費的最少金幣是多少?輸入輸出格式
輸入格式:
第一行包含兩個正整數 m, n,以一個空格分開,分別代表棋盤的大小,棋盤上有顏色的格子的數量。
接下來的 n n行,每行三個正整數 x, y, c, 分別表示座標為(x,y)的格子有顏色 c。
其中 c=1 代表黃色, c=0 代表紅色。 相鄰兩個數之間用一個空格隔開。 棋盤左上角的座標為(1, 1),右下角的座標為(m,m)。
棋盤上其餘的格子都是無色。保證棋盤的左上角,也就是(1,1) 一定是有顏色的。輸出格式:
一個整數,表示花費的金幣的最小值,如果無法到達,輸出-1。
輸入輸出樣例
輸入樣例#1:
5 7
1 1 0
1 2 0
2 2 1
3 3 1
3 4 0
4 4 1
5 5 0輸出樣例#1:
8
輸入樣例#2:
5 5
1 1 0
1 2 0
2 2 1
3 3 1
5 5 0輸出樣例#2:
-1
資料規模與約定
對於 30%的資料, 1 ≤ m ≤ 5, 1 ≤ n ≤ 10。
對於 60%的資料, 1 ≤ m ≤ 20, 1 ≤ n ≤ 200。
對於 100%的資料, 1 ≤ m ≤ 100, 1 ≤ n ≤ 1,000。
思路:歷年普及組第三題,並不算難,這道題就是個四向搜尋,直接掃描所有的格子,記得剪枝!!!
程式碼(附註釋):
#include <bits/stdc++.h>
using namespace std;
const int maxn=2000+10,INF=2147483646;
int ans,dx[4]={-1,0,1,0},dy[4]={0,-1,0,1}/*四個方位*/,f[maxn][maxn]/*儲存圖表*/,a[maxn][maxn]/*用於剪枝*/,n,m,x,y,c;
void dfs(int x,int y/*x,y座標*/,int sum/*當前的總錢數*/,bool magic/*是否用了魔法(~~膜法~~)*/){
if(x>m||y>m||x<1||y<1)//超界就直接返回
return;
if(sum>=a[x][y])//重點剪枝!!!如果以前的最優還比當前的走法更優,就不用找了,毫無意義。
return;
a[x][y]=sum;//將a[x][y]替換為當前的最大sum值
if(x==m&&y==m){//如果到達了最後一格
ans=min(ans,sum);//那麼就判斷到達最後一格所用的錢數sum是否小於所有方案的最小值ans,如果小就替換
return ;
}
for(int i=0;i<4;i++){
int xx=dx[i]+x;//新的x值
int yy=dy[i]+y;//新的y值
if(f[xx][yy]){//如果是有顏色的
if(f[xx][yy]==f[x][y])//如果顏色相同,直接不廢錢,走過去就是了
dfs(xx,yy,sum,false);//繼續搜尋
else
dfs(xx,yy,sum+1,false);//換個顏色
}
else if(!f[xx][yy] && !magic){//如果必須用魔法且可以用魔法
f[xx][yy]=f[x][y];//替換
dfs(xx,yy,sum+2,true);//繼續搜尋
f[xx][yy]=0;//回溯一步
}
}
}
int main(){
ans=INF;//現將ans設為無窮大
memset(a,0x3f,sizeof(a));//初始化
scanf("%d%d",&m,&n);
for(int i=1;i<=n;i++){
scanf("%d%d%d",&x,&y,&c);//輸入
f[x][y]=c+1;//將f[x][y]存為顏色
}
dfs(1,1,0,false);//開始搜尋
if(ans==INF)//如果不能走到最後一格
cout<<-1;//輸出-1
else//否則有解
cout<<ans;//輸出最優解ans
return 0;//圓滿結束!!!
}