棋盘游戏
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;
}