2015 UESTC Training for Dynamic Programming 男神的约会 BFS+状压DP

D - 男神的约会
Time Limit:1000MS     Memory Limit:65535KB     64bit IO Format:%lld & %llu
Appoint description: 

Description

有一天男神约了学姐姐去看电影,电影院有一个活动,给你一个10*10的矩阵,每一个格子上都有一个0-9的整数,表示一共十种优惠券中的一种。

观众从左上角的格子开始走,走到右下角。每走到一个有着a号优惠券的格子,都必须要玩一个a分钟的游戏来领取这张优惠券。

每次只能向右或向下走。当走到右下角的时候,如果集齐10种优惠券就可以半价看电影呢。

为了能在学姐姐面前展示自己的才智,男神准备用最少的时间领取全部的优惠券(他要省出最多的时间陪学姐姐)。聪明的你能告诉男神,他最少要花费的时间是多少?

Input

输入包含10行,每行10个数字,以空格隔开,表示格子上的优惠券的种类。数据保证存在合法路径。

Output

输出男神走到右下角的最小时间花费。

Sample Input

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

Sample Output

50

Hint

题意:从(0,0)到(9,9)要求只能想右或者向下并领取所有的优惠券(各种不同的数字代表不同的优惠券),求最少所用的时间。

思路:从最少的时间大概能想到有BFS,但是对于判断当前领取的优惠卷就百思不得其解了,最后看了一下别人的思路才略有思路,因为每次只能向下或者向右,所以当前状态(i,j)只与(i-1,j)和(i,j-1)有关,所以就可以得出状态转移方程:dp[i][j][k|(1<<g[i][j])] = min(dp[i-1][j][k],dp[i][j-1][k])+g[i][j];(i-1,j)和(i,j-1)保证在范围内。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <string>
#include <queue>
#include <cmath>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
int g[12][12],dp[12][12][1<<11];
//dp[i][j][k]表示从坐标(0,0)走到当前坐标(i,j)而且当前状态为k时所使用的最少时间
int dir[2][2] = {{1,0},{0,1}};//两个方向(右或下)
bool vis[12][12][1<<11];//标记每个点是否走过
struct Point
{
	int x,y,k;
	Point(int _x = 0,int _y = 0,int _k = 0):x(_x),y(_y),k(_k){};
};
void BFS()
{
	memset(vis,false,sizeof(vis));
	memset(dp,inf,sizeof(dp));
	Point head,next;
	queue<Point>q;
	Point p(0,0,1<<g[0][0]);
	vis[p.x][p.y][p.k] = true;
	dp[p.x][p.y][p.k] = g[p.x][p.y];
	q.push(p);
	while(!q.empty())
	{
		head = q.front(); q.pop();
		for(int i = 0; i<2; i++)//两个方向(右或下)
		{
			next.x = head.x + dir[i][0];
			next.y = head.y + dir[i][1];
			if(next.x >= 0 && next.x <10 && next.y >= 0 && next.y <10)//判断是否出界了
			{
				next.k = head.k|(1<<g[next.x][next.y]);//每走一步就把当前优惠券号标记住
				dp[next.x][next.y][next.k] = min(dp[next.x][next.y][next.k],dp[head.x][head.y][head.k]+g[next.x][next.y]);
				if(!vis[next.x][next.y][next.k])//判断当前坐标是否走过了
				{
					q.push(next);
					vis[next.x][next.y][next.k] = true;
				}
			}
		}
	}
}
int main()
{
#ifdef CDZSC_June
	freopen("t.txt","r",stdin);
#endif
	for(int i = 0; i<10; i++)
	{
		for(int j = 0; j<10; j++)
		{
			scanf("%d",&g[i][j]);
		}
	}
	BFS();
	printf("%d\n",dp[9][9][(1<<10) - 1]);//(1<<10) - 1)代表的是全部的优惠卷都领取了
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值