现在的位置: 首页 > 综合 > 正文

【状压DP】棋盘游戏 board

2014年03月14日 ⁄ 综合 ⁄ 共 1469字 ⁄ 字号 评论关闭

棋盘游戏

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;
}

抱歉!评论已关闭.