棋盘游戏
board.pascal/c/cpp
【题目描述】
这个游戏在一个有10*10个格子的棋盘上进行,初始时棋子位于左上角,终点为右下角,棋盘上每个格子内有一个0到9的数字,每次棋子可以往右方或下方的相邻格子移动,求一条经过数字之和最小且经过0到9的所有数字的合法路径,输出其长度。(经过的数字包括左上角和右下角)
【输入】
输入包含10行,每行10个数字,以空格隔开,表示棋盘格子上的权值。数据保证存在合法路径。
【输出】
输出所求路径的权值和。
【输入样例】
0 1 2 3 4 5 6 7 8 9
1 1 1 1 1 1 1 1 1 0
2 1 1 1 1 1 1 1 1 0
3 1 1 1 1 1 1 1 1 0
4 1 1 1 1 1 1 1 1 0
5 1 1 1 1 1 1 1 1 0
6 1 1 1 1 1 1 1 1 0
7 1 1 1 1 1 1 1 1 0
8 1 1 1 1 1 1 1 1 0
9 1 1 1 1 1 1 1 1 5
【输出样例】
50
【样例解释】
先一直向右走到第一行末尾,再竖直向下走位最优路径。
完成情况:
这一题一个很直观的想法就是搜!
但是我们看题目中有这样一句话——只能向右和向下走,这就提醒了无后效性,满足动态规划
既然是动态规划,就要设计状态,我们先不管经过0~9那些点,就是从左上走到右下的最小权值和
我们就可以用 f [ x ] [ y ] 表示当前在点 ( x , y ) 处所得的最优值
方程就可以写为 f [ x ] [ y ] = min ( f [ x - 1 ] [ y ] , f [ x ] [ y - 1 ] ) ;
然后我们加上状态,由于走到点 ( x , y ) 可以由不同点转移而来,也就是一个点的状态不同,它的最优值也可能不同
而同理不同的状态对后面点的影响也不同
一个最容易想到的方法是在后面加上十维,分别表示当前点0~9是否走过
但是这样问题就来了,这样写是肯定可以过的,但是我们就需要写N多个 if 来判断,并且更新走过的点的状态的时候也要写N多个 if
由于后面十维都只有0和1,那么我们可以把后面的十位数看成一个二进制数,然后转换成十进制数就可以在从十二维的状态变成三维状态了
更新状态的时候我们可以用到位运算的按位或
不过最终递推的写法写昏了,所以就换成了递归的写法,不过思想是一样的
C++ AC Code
/* C++ Code http://blog.csdn.net/jiangzh7 By Jiangzh */ #include<cstdio> #include<cstring> #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) const int inf=0x3f3f3f3f; int map[20][20]; int f[20][20][2000]; int ans=inf; void read() { freopen("board.in","r",stdin); freopen("board.out","w",stdout); for(int i=1;i<=10;i++) for(int j=1;j<=10;j++) scanf("%d",&map[i][j]); } void dfs(int x,int y,int S,int sum) { if(x<1||x>10||y<1||y>10) return; int st=S|(1<<map[x][y]); if(x==10 && y==10) { if(st==1023) ans=min(ans,sum+map[10][10]); return; } dfs(x+1,y,st,sum+map[x][y]); dfs(x,y+1,st,sum+map[x][y]); } void work() { dfs(1,1,0,0); printf("%d\n",ans); } int main() { read(); work(); return 0; }