BestCoder14 1002.Harry And Dig Machine(hdu 5067) 解题报告

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5067

题目意思:给出一个 n * m 的方格,每一个小方格(大小为1*1)的值要么为 0 要么为一个正整数。规定是正整数的值得方格个数不超过 10 个。现在问从最左上角的点开始,要经过所有正整数的值的点之后,要返回到最左上角的点的最短路径是多少。

其实为正整数的那些点具体值是什么根本无关紧要。可以先求出所有点到其他点的两两最短路径,然后利用状态压缩 (考虑到只有10个点,状态数最多为2^10)来求出答案),题解是这样说的:

dp[i][j]:表示状态 i 的点被访问过了,当前停留在点 j 需要的最少时间。枚举另一个不在状态 i 内的点 k , 从点 j 走到点 k 的状态转移方程为:

dp[i|(1<<k)][k] = min(dp[i|(1<<k)][k], dp[i][j] + dist(j, k))

其中,dist[j][k] 表示从点 j 与 点 k 的最短距离。所以可以利用 bfs(当然有更简单的解法,这里我是为了练手而已) 求出以每一个点作为出发点,求出以这个点到其他所有点的最短距离。

代码中的 dp 计算,和方程有一点点不同。有些地方不太理解,先留着吧。。。

  1 #include <cstdio>
  2 #include <cstdlib>
  3 #include <cstring>
  4 #include <iostream>
  5 #include <algorithm>
  6 #include <queue>
  7 using namespace std;
  8
  9 const int maxn = 50 + 5;
 10 const int INF = 0x3f3f3f3f;
 11 int n, m, cnt, st[maxn], end[maxn];
 12 int vis[maxn][maxn], hs[maxn][maxn];
 13 int dp[maxn][1<<13], dist[maxn][maxn];
 14
 15 int dx[] = {0, 1, 0, -1};
 16 int dy[] = {1, 0, -1, 0};
 17
 18 struct node
 19 {
 20     int x, y;
 21     int step;
 22 } cur, tmp;
 23
 24 queue<node> q;
 25
 26 void bfs(int id)
 27 {
 28     while (!q.empty())
 29         q.pop();
 30     memset(vis, 0, sizeof(vis));
 31     tmp.x = st[id], tmp.y = end[id];
 32     tmp.step = dist[id][id] = 0;
 33     q.push(tmp);
 34     vis[st[id]][end[id]] = 1;
 35
 36     while (!q.empty())
 37     {
 38         tmp = q.front();
 39         q.pop();
 40
 41         for (int i = 0; i < 4; i++)
 42         {
 43             int tx = tmp.x + dx[i];
 44             int ty = tmp.y + dy[i];
 45             if (tx >= 1 && tx <= n && ty >= 1 && ty <= m && !vis[tx][ty])
 46             {
 47                 vis[tx][ty] = 1;
 48                 cur.x = tx;
 49                 cur.y = ty;
 50                 cur.step = tmp.step + 1;
 51                 if (hs[tx][ty] != -1)      // the target point‘s id
 52                     dist[id][hs[tx][ty]] = cur.step;
 53                 q.push(cur);
 54             }
 55         }
 56     }
 57 }
 58
 59 void Init()
 60 {
 61     memset(hs, -1, sizeof(hs));
 62     int val;
 63     cnt = 0;
 64     for (int i = 1; i <= n; i++)
 65     {
 66         for (int j = 1; j <= m; j++)
 67         {
 68             scanf("%d", &val);
 69             if (val || i == 1 && j == 1)
 70             {
 71                 st[cnt] = i;
 72                 end[cnt] = j;
 73                 hs[i][j] = cnt++;  // 为每一个大于0的点(最多只有10个)编号,第一个点编号为0而不是1
 74             }
 75         }
 76     }
 77 }
 78
 79 int main()
 80 {
 81 #ifndef ONLINE_JUDGE
 82     freopen("in.txt", "r", stdin);
 83 #endif
 84
 85     while (scanf("%d%d", &n, &m) != EOF)
 86     {
 87         Init();
 88         for (int i = 0; i < cnt; i++)   // the shortest path from point i to other points
 89             bfs(i);
 90         int status = 1<<cnt;
 91         memset(dp, 0x3f, sizeof(dp));
 92
 93         dp[0][0] = 0;
 94         for (int i = 0; i < status; i++)
 95         {
 96             for (int j = 0; j < cnt; j++)
 97             {
 98                 if (dp[j][i] != INF)
 99                 {
100                     for (int k = 0; k < cnt; k++)
101                     {
102                         if (!(i & (1<<k)))   // 点k不包含在状态i上
103                             dp[k][i|(1<<k)] = min(dp[k][i|(1<<k)], dp[j][i]+dist[j][k]);
104                     }
105                 }
106             }
107         }
108         printf("%d\n", dp[0][status-1]);
109     }
110     return 0;
111 }

代码中最后的输出 dp[0][status-1] 很明显就是从最左上角的点出发,经过所有正整数点的最短距离。不过为什么求得时候与二维数组dp[][]不同,这个确实不太理解。

时间: 2024-10-11 23:44:24

BestCoder14 1002.Harry And Dig Machine(hdu 5067) 解题报告的相关文章

BestCoder16 1002.Revenge of LIS II(hdu 5087) 解题报告

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5087 题目意思:找出第二个最长递增子序列,输出长度.就是说,假如序列为 1 1 2,第二长递增子序列是1 2(下标为2 3),而第一长递增子序列也是(下标为 1 3). 我一开始天真的以为,还是利用求最长递增子序列的算法啦,第二长不就是对dp 数组sort完后(从小到大)的dp[cnt-1] 啦,接着就呵呵啦----= = 题解说,要加多一个 dp 数组,以便对当前下标值为 i 的数 a[i] 为结

BestCoder22 1002.NPY and arithmetic progression(hdu 5143) 解题报告

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5143 题目意思:给出 1, 2, 3, 4 的数量,分别为a1, a2, a3, a4,问是否在每个数只使用一次的前提下,分成若干部分,每部分数的长度 >= 3且满足是等差数列.可以的话输出 Yes ,否则 No . 比赛的时候过了pretest,那个开心啊--然后跟 XX 兽讨论了之后,才发现自己理解错了题目= = 要用到深搜,问组合嘛--组合就是有可能是(1, 2, 3, 4).(1, 2, 3

hdu 2112 HDU Today 解题报告

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2112 题目意思:又是求最短路的,不过结合埋字符串来考查. 受之前1004 Let the Balloon Rise 学到用map 的解法做之后,有点蠢蠢欲动,当时见到要用字典树做有点吓坏了(之前看过下,非一般难理解,所以暂时放下了).于是,死就死吧,硬住头皮用map做,反反复复修改终于过了. 首先是memory limit exceeded,因为无读清题目意思,直接开10000 * 10000的数组

BestCoder17 1002.Select(hdu 5101) 解题报告

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5101 题目意思:给出 n 个 classes 和 Dudu 的 IQ(为k),每个classes 都有相应的人,每个人又有相应的IQ.现在要求从这些classes挑选两个人,满足IQ之和 > k,不过要满足这两个人不能来自同一个class的. 解题思路不难想出,直接所有人两两之和 > k - 同一个class 两两之和 > k  就是答案了. 不过很容易超时!!!! 用二分就可以过了.二分有

BestCoder12 1002.Help him(hdu 5059) 解题报告

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5059 题目意思:就是输入一行不多于 100 的字符串(除了'\n' 和 '\r' 的任意字符),问是否是合法的整数,如果是的话问是否在[a, b] 范围内,是则输出 YES,否则输出 NO 合法的整数:(1)非负整数:只有数字,没有前导0 (2)负数:负号后面只能跟着数字,负号前没有任何字符 首先这条题感觉不是出得太好,不过都是硬着头皮学人家做啦.反正一些很变态的数据可能还是过不了,但却AC的. 模

BestCoder3 1002 BestCoder Sequence(hdu 4908) 解题报告

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4908 题目意思:给出 一个从1~N 的排列你和指定这个排列中的一个中位数m,从这个排列中找出长度为奇数,中位数是m的子序列有多少个. 我的做法被discuss 中的测试数据一下子就否定了. 这个是别人的做法,暂时留下来,有些地方还没真正弄懂,应该是缺了部分的知识没有学到... 留着先: (1)http://blog.csdn.net/hcbbt/article/details/38377815 (2

BestCoder15 1002.Instruction(hdu 5083) 解题报告

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5083 题目意思:如果给出 instruction 就需要输出对应的 16-bit binary code,给出16-bit binary code 就需要输出对应的instruction. 由于不会截取的技巧,代码量非常可观 = =,所以说,一直很讨厌做模拟题!!! 留下这代码,纪念一个代码还是不够精简的自己!!!内存和时间还能接受,也比较容易理解,不过好多重复代码= =.以下这个可以代码可以忽略,

BestCoder20 1002.lines (hdu 5124) 解题报告

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5124 题目意思:给出 n 条线段,每条线段用两个整数描述,对于第 i 条线段:xi,yi 表示该条线段的左端点和右端点.设 A 表示最多线段覆盖的点(当然这个 A 可以有多个啦,但这个无关紧要).现在需要求的是 A 被多少条线段覆盖. 我是不会做啦.......一开始还看不懂题目= = 以下是按照题解做的, 题解中的最大区间和,实际上就是求最大连续子序列的和. 1 #include <iostrea

BestCoder7 1002 Little Pony and Alohomora Part I(hdu 4986) 解题报告

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4986 题目意思:有 n 个box(从左到右编号依次为1~n),每个box里面有一个随机的钥匙,有可能这条钥匙恰好可以开到这个box,但大多数情况下是不能够的.问期望值是多少.(例如对于两个box,有可能装着1 2 或者 2 1的 key,如果是1 2,那么就需要用两次咒语,而对于2 1(打开其中一个box就可以得到要开到另一个box的钥匙)只需要用一次即可.期望值就是 1/2 * 2 + 1/2 *