[SMOJ1176]送礼物
来源:互联网 发布:淘宝广告 编辑:程序博客网 时间:2024/04/29 16:11
题目描述
给出一个
n 行m 列的点阵,“.”表示可通行格子,“#”表示不可通行格子,“K”表示国王的初始位置,“Q”表示王后的位置,“G”表示该格子有一个礼 物。注意:国王、王后、礼物所在的格子可以认为是可通行格子。国王从开始位置出发,国王从当前格子可以走到上、下、左、右四个相邻格子,当然前提是可通行 格子。国王从当前格子走到相邻格子的时间是变化的,这取决于国王手头上收集到的礼物的数量,假如当前国王手头上有y 个礼物,那么他从当前格子移动到相邻格 子的所用时间是y+1 秒。一旦国王进入某个有礼物的格子,他可以选择取该格子的礼物,也可以选择不取该格子的礼物。取礼物这个动作可以认为是瞬间完成的, 不需要时间。国王想收集到尽量多的礼物送给王后,但是他到达王后所在的格子不能超过T 秒,王后不想等太长时间。注意:国王在收集礼物的途中可能多次走到相 同的格子。
输入格式 1776.in
第 1 行:三个整数
n 、m 、T 。1≤n ,m≤50 ,1≤T≤109 。
接下来是n 行m 列的点阵,‘G’的数量不超过16。只有一个国王,一个王后。
输出格式 1776.out
一个整数,国王在规定时间内,最多可以收集到多少个礼物送给王后?
样例输入 1776.in
5 7 50
#....G#
###G###
#K...Q#
###.###
#G..GG#
样例输出
4
被这题虐得痛并快乐着……编码量应该算是这几天写的最多的吧(反正我写了3k),在本地也调了好半天,不过一交就过了,还是挺开心的。
还是一样,我们考虑一下这题当中哪些因素应该成为状态。如果是基于位置?显然不大可能吧,那样就太大了。秉承我校教练蓝老师一贯的“数据量决定算法”理念,我发现了礼物的数量
(归纳一下,以后如果遇到题目中某些东西是开或关,取或不取,经过或不经过……等这种“01”类的状态时,就可以往状压 DP 的方向想想,当然还是要考虑一下阶段、子问题和后效性)
题目问的是限定时间内能取的最多礼物数,既然我们都以礼物为状态了,怎么可能还求的是数量呢?那么,要 DP 求的必然是另一个条件——时间了。
不妨设
原题中对于耗时的描述是“假如当前国王手头上有
那么就有
上面这个转移方程的意思就是,从某一个取了礼物
这个式子的正确性是显然的。但是我们在做 DP 的时候存在一个效率上的问题。设礼物个数为
对于
主要是
还有一个问题,就是国王和皇后。那其实我们不妨将他们也看作两个礼物,统一化处理就可以了。国王的编号定为 0,皇后为
或者还有一种思路(我就是这么写的),不过比较麻烦,就是预处理的时候从国王开始, DP 的时候把国王和皇后去除,最后取答案的时候再枚举每种情况到皇后,这种写法相对来说没有那么鲁棒。
参考代码:
#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <queue>using namespace std;//用pair存坐标,改成习惯的形式#define X first#define Y secondconst int maxn = 50 + 10;const int maxg = 18;const int dirx[] = {-1, 1, 0, 0};const int diry[] = {0, 0, -1, 1};typedef pair <int, int> Point;int n, m, T;char map[maxn][maxn];int dp[1 << maxg][maxg];int cnt = 0;Point gift[maxg]; //礼物的坐标int disPoint[maxn][maxn];//disPoint[i,j]为某点到(i,j)的最短距离,bfs时临时用int disGift[maxg][maxg]; //两礼物间的最短距离,dp时计算用inline bool islegal(int x, int y) { //坐标越界判定 return x >= 0 && x < n && y >= 0 && y < m;}bool visited[maxn][maxn]; //是否访问过void bfs(Point p, int num) { //p为起点,num为起点在gift[]中的下标 queue <Point> q; while (!q.empty()) q.pop(); memset(visited, false,sizeof visited); visited[p.X][p.Y] = true; memset(disPoint, 0, sizeof disPoint); for (q.push(p); !q.empty(); q.pop()) { Point cur = q.front(); for (int i = 0; i < 4; i++) { int newx = cur.X + dirx[i]; int newy = cur.Y + diry[i]; if (islegal(newx, newy) && !visited[newx][newy] && map[newx][newy] != '#') { disPoint[newx][newy] = disPoint[cur.X][cur.Y] + 1; q.push(make_pair(newx, newy)); visited[newx][newy] = true; } } } //算出并保存从当前情况的起始礼物到其他各礼物的最短路 for (int i = 0; i < cnt; i++) disGift[num][i] = disPoint[gift[i].X][gift[i].Y];}#define lowbit(x) (x & -x)int count(int x) { //求某个数的二进制中1的个数 int ret = 0; while (x) { x -= lowbit(x); ++ret; } return ret;}int main(void) { freopen("1776.in", "r", stdin); freopen("1776.out", "w", stdout); scanf("%d%d%d\n", &n, &m, &T); Point king, queen; for (int i = 0; i < n; i++) { gets(map[i]); //puts(map[i]); for (int j = 0; j < m; j++) if (map[i][j] == 'G') gift[cnt++] = make_pair(i, j); else if (map[i][j] == 'K') king = make_pair(i, j); else if (map[i][j] == 'Q') queen = make_pair(i, j); } gift[cnt++] = king; gift[cnt++] = queen; //for (int i = 0; i < cnt; i++) printf("%d %d\n", gift[i].X, gift[i].Y); memset(disGift, 0, sizeof disGift); for (int i = 0; i + 1 < cnt; i++) bfs(gift[i], i); /* for (int i = 0; i + 1 < cnt; i++) { for (int j = 0; j + 1 < cnt; j++) printf("%d ", disGift[i][j]); putchar('\n'); } */ int kingNum = cnt - 2; //国王(作为礼物)的下标 int upperLim = 1 << kingNum; //DP中枚举状态时不考虑国王和皇后 memset(dp, 0x7f, sizeof dp); for (int i = 0; i < kingNum; i++) { //初始化,国王到各个礼物的花费 dp[1 << i][i] = disGift[kingNum][i]; //printf("%d %d %d\n", 1 << i, i, dp[1 << i][i]); } //puts("prework done"); for (int i = 0; i < upperLim; i++) for (int j = 0; j < kingNum; j++) if ((1 << j) & i) for (int k = 0; k < kingNum; k++) if (j != k && ((1 << k) & i) && disGift[j][k]) { dp[i][j] = min(dp[i][j], dp[i - (1 << j)][k] + count(i) * disGift[j][k]); //printf("%d %d %d %d\n", i, j, k, dp[i][j]); } int queenNum = cnt - 1; int ans = 0; for (int i = 0; i < upperLim; i++) for (int j = 0; j < kingNum; j++) //枚举最后取的礼物 if (disGift[j][queenNum]) { //该礼物要可达皇后才是合法方案 int cost = dp[i][j] + (count(i) + 1) * disGift[j][queenNum]; if (cost <= T) ans = max(ans, count(i)); } printf("%d\n", ans); return 0;}
- [SMOJ1176]送礼物
- 送礼物
- 送礼物
- tyvj p1340 送礼物
- 【JSOI2015】送礼物
- 谁在圣诞节送礼物?
- 送礼物送什么好
- [单调队列] gift 送礼物
- tyvj1340送礼物 折半枚举
- 现身说法,如何给上司送礼物
- 2010年结婚送礼物攻略!
- usaco Greedy Gift Givers (送礼物)
- 公众号微信支付直播室送礼物
- 濮阳,明天母亲节,怎么送礼物?团购送礼物。相关资料。20160507
- 男女之间互送礼物的含义大全 别送错了
- html5游戏开发-零基础开发《圣诞老人送礼物》小游戏
- html5游戏开发-零基础开发《圣诞老人送礼物》小游戏
- 杨千嬅获爱子送礼物 外出时不顾疲倦亲手抱
- 利用Robotics Toolbox创建一个机器人
- bootstrap 下箭头的禁用
- ACE在linux下的安装和实例运行详细步骤
- 数据结构之AVL树
- Linux/UNIX下使用ssh-keygen设置SSH无密码登录
- [SMOJ1176]送礼物
- 递归查询无限级分类最底层分类
- 2-尾部的零
- 推荐经管之家论坛
- elasticsearch中的mapping简介
- matlab输入输出格式
- iOS 获取设备唯一标示符的方法[链接文章]
- 让TD单元格内的超长文字换行显示不影响表格整体效果
- RSA加密解密及证书