題目描述
有一個m \times mm×m的棋盤,棋盤上每一個格子可能是紅色、黃色或沒有任何顏色的。你現在要從棋盤的最左上角走到棋盤的最右下角。
任何一個時刻,你所站在的位置必須是有顏色的(不能是無色的), 你只能向上、 下、左、 右四個方向前進。當你從一個格子走向另一個格子時,如果兩個格子的顏色相同,那你不需要花費金幣;如果不同,則你需要花費 11個金幣。
另外, 你可以花費 22 個金幣施展魔法讓下一個無色格子暫時變為你指定的顏色。但這個魔法不能連續使用, 而且這個魔法的持續時間很短,也就是說,如果你使用了這個魔法,走到了這個暫時有顏色的格子上,你就不能繼續使用魔法; 只有當你離開這個位置,走到一個本來就有顏色的格子上的時候,你才能繼續使用這個魔法,而當你離開了這個位置(施展魔法使得變為有顏色的格子)時,這個格子恢復為無色。
現在你要從棋盤的最左上角,走到棋盤的最右下角,求花費的最少金幣是多少?
輸入輸出格式
輸入格式:
第一行包含兩個正整數m, nm,n,以一個空格分開,分別代表棋盤的大小,棋盤上有顏色的格子的數量。
接下來的nn行,每行三個正整數x, y, cx,y,c, 分別表示坐標為(x,y)(x,y)的格子有顏色cc。
其中c=1c=1 代表黃色,c=0c=0 代表紅色。 相鄰兩個數之間用一個空格隔開。 棋盤左上角的坐標為(1, 1)(1,1),右下角的坐標為( m, m)(m,m)。
棋盤上其余的格子都是無色。保證棋盤的左上角,也就是(1, 1)(1,1) 一定是有顏色的。
輸出格式:
一個整數,表示花費的金幣的最小值,如果無法到達,輸出-1−1。
輸入輸出樣例
說明
輸入輸出樣例 1 說明
從(1,1)(1,1)開始,走到(1,2)(1,2)不花費金幣
從(1,2)(1,2)向下走到(2,2)(2,2)花費 11 枚金幣
從(2,2)(2,2)施展魔法,將(2,3)(2,3)變為黃色,花費 22 枚金幣
從(2,2)(2,2)走到(2,3)(2,3)不花費金幣
從(2,3)(2,3)走到(3,3)(3,3)不花費金幣
從(3,3)(3,3)走到(3,4)(3,4)花費 11 枚金幣
從(3,4)(3,4)走到(4,4)(4,4)花費 11 枚金幣
從(4,4)(4,4)施展魔法,將(4,5)(4,5)變為黃色,花費22 枚金幣,
從(4,4)(4,4)走到(4,5)(4,5)不花費金幣
從(4,5)(4,5)走到(5,5)(5,5)花費 11 枚金幣
共花費 88枚金幣。
輸入輸出樣例 2 說明
從( 1, 1)(1,1)走到( 1, 2)(1,2),不花費金幣
從( 1, 2)(1,2)走到( 2, 2)(2,2),花費11金幣
施展魔法將( 2, 3)(2,3)變為黃色,並從( 2, 2)(2,2)走到( 2, 3)(2,3)花費22 金幣
從( 2, 3)(2,3)走到( 3, 3)(3,3)不花費金幣
從( 3, 3)(3,3)只能施展魔法到達( 3, 2),( 2, 3),( 3, 4),( 4, 3)(3,2),(2,3),(3,4),(4,3)
而從以上四點均無法到達( 5, 5)(5,5),故無法到達終點,輸出-1−1
數據規模與約定
對於 330%的數據, 1 ≤ m ≤ 5, 1 ≤ n ≤ 101≤m≤5,1≤n≤10。
對於 60%的數據, 1 ≤ m ≤ 20, 1 ≤ n ≤ 2001≤m≤20,1≤n≤200。
對於 100%的數據, 1 ≤ m ≤ 100, 1 ≤ n ≤ 1,0001≤m≤100,1≤n≤1,000。
解析:
看到這個程序我首先想到的就是深度優先搜索,這樣只能過部分數據,其他超時(TLE)。

#include<iostream> using namespace std; int chess[110][110]={0}; int flag[110][110]={0}; int dx[5]={-1,0,0,1}; int dy[5]={0,1,-1,0}; int m,n,total=100100; int dgp(int x,int y,int color,int sum){ if(x==m&&y==m&&sum<total){total=sum; return 0;} for(int i=1;i<=4;i++){ int xx=x+dx[i],yy=y+dy[i]; if(!flag[xx][yy]&&chess[xx][yy]!=-2&&(chess[x][y]!=0||chess[xx][yy]!=0)){ flag[xx][yy]=1; if(chess[xx][yy]==chess[x][y]||(chess[x][y]==0&&color==chess[xx][yy]))dgp(xx,yy,chess[x][y],sum); else if(chess[xx][yy]==0)dgp(xx,yy,chess[x][y],sum+2); else dgp(xx,yy,chess[x][y],sum+1); flag[xx][yy]=0; } } } int main(){ int x,y,c; cin>>m>>n; for(int i=0;i<=m+1;i++) chess[i][0]=chess[0][i]=chess[m+1][i]=chess[i][m+1]=-2; for(int i=1;i<=m;i++) for(int j=1;j<=m;j++) chess[i][j]=0; for(int i=1;i<=n;i++){ cin>>x>>y>>c; chess[x][y]=c+1; } flag[1][1]=1; dgp(1,1,0,0); if (total==100100) cout <<-1; else cout<<total; return 0; }
於是我添加了2個剪枝,竟然全部Ac,由此得出結論“暴力出奇跡,剪枝少不了!!!”

#include<iostream> using namespace std; int chess[1100][1100]={0};// 棋盤 int flag[1100][1100]={0};//訪問標記 int dist[1100][1100]={0}; //記錄最短距離 int dx[5]={-1,0,0,1}; int dy[5]={0,1,-1,0}; int inf=1234567890; int m,n,total=inf; int dfs(int x,int y,int color,int sum){//記憶化搜索 if (sum>=dist[m][m]) return 0;//剪枝1,如果到達當前點的距離已經大於等於終點 if (dist[x][y]<=sum) return 0;//剪枝2,如果到達當前點的距離不是當前最優。 dist[x][y]=sum;//如果到達當前點的距離是當前最優 if(x==m&&y==m&&sum<dist[m][m]){dist[m][m]=sum; return 0;}//如果到達終點 for(int i=1;i<=4;i++){ int xx=x+dx[i],yy=y+dy[i]; if(!flag[xx][yy]&&chess[xx][yy]!=-2&&(chess[x][y]!=0||chess[xx][yy]!=0)){ flag[xx][yy]=1;//已經訪問標記 if(chess[xx][yy]==chess[x][y]||(chess[x][y]==0&&color==chess[xx][yy])){dfs(xx,yy,chess[x][y],sum);} else if(chess[xx][yy]==0){dfs(xx,yy,chess[x][y],sum+2);} else {dfs(xx,yy,chess[x][y],sum+1);} flag[xx][yy]=0;//撤銷訪問標記,回溯。 } } } int main(){ int x,y,c; cin>>m>>n; for(int i=0;i<=m+1;i++) chess[i][0]=chess[0][i]=chess[m+1][i]=chess[i][m+1]=-2;//邊界 for(int i=1;i<=m;i++) for(int j=1;j<=m;j++) {chess[i][j]=0;dist[i][j]=inf;} for(int i=1;i<=n;i++){ cin>>x>>y>>c; chess[x][y]=c+1; } flag[1][1]=1;//起點 dfs(1,1,0,0); if (dist[m][m]==inf) cout <<-1; else cout<<dist[m][m]; return 0; }