题目链接【http://codevs.cn/problem/1358/】
题意:这个游戏在一个有10*10个格子的棋盘上进行,初始时棋子位于左上角,终点为右下角,棋盘上每个格子内有一个0到9的数字,每次棋子可以往右方或下方的相邻格子移动,求一条经过数字之和最小且经过0到9的所有数字的合法路径,输出其长度。(经过的数字包括左上角和右下角)。
思路一:深搜(DFS),因为他只可以向左走或又走,那么每次有两个方向,走到终点的时候,最多走了20步,所以深度最深为20,可解。
#include<cstdio> #include<cstring> #include<algorithm> const int inf = 1e9 + 10; using namespace std; int a[15][15]; int ans = inf; void DFS(int x, int y, int k, int sum) { if(x < 1 || x > 10 || y < 1 || y > 10) return ; k |=(1 << a[x][y]); sum += a[x][y]; if(x == 10 && y == 10 && k == 1023) ans = min(ans, sum); DFS(x + 1, y, k, sum); DFS(x, y + 1, k, sum); } int main () { for(int i = 1; i <= 10; i++) for(int j = 1; j <= 10; j++) scanf("%d", &a[i][j]); DFS(1, 1, 0, 0); printf("%d ", ans); }
思路二:状压DP,dp[i][j][s],表示,要走(i,j)这个格子,s[0,(1<<10)-1],如果s中的第i位为1,表示到达dp[i][j][s]这个状态的时候数字i已经被访问过了,如果为0那么表示数字i没有被访问过,最后输出dp[10][10][(1<<10)-1];
#include<cstdio> #include<cstring> #include<algorithm> const int inf = 1e9 + 10; using namespace std; int a[15][15]; int dp[15][15][1 << 10]; int main () { for(int i = 1; i <= 10; i++) for(int j = 1; j <= 10; j++) scanf("%d", &a[i][j]); memset(dp,0x3f3f3f,sizeof(dp)); for(int i = 1; i <= 10; i++) for(int j = 1; j <= 10; j++) { if(i == 1 && j == 1) { dp[i][j][1 << a[i][j]] = a[i][j]; continue; } for(int k = 1; k < (1 << 10); k++) { if(k & (1 << a[i][j])) { if(i > 1) dp[i][j][k] = a[i][j] + min(dp[i - 1][j][k - (1 << a[i][j])], dp[i - 1][j][k]); if(j > 1) dp[i][j][k] = min(dp[i][j][k], a[i][j] + min(dp[i][j - 1][k - (1 << a[i][j])], dp[i][j - 1][k])); } } } printf("%d ", dp[10][10][(1 << 10) - 1]); }