题目: UVA - 10599Robots(II)(LIS)
题目大意:一个N * M 的矩阵,上面有些格子上有垃圾,现在要求一个机器人从1,1的格子出发,往右或是往下走最终到达N * M各格子,沿途要收集最多的垃圾。现在将垃圾编号,要求输出最多能清理的垃圾并且输出这样的清理路线有多少条,输出其中字典序最小的那一条。
解题思路:一开始还以为是简单的dp,结果输出发现路径多了好多条,才发现题意理解的有问题,它只计算捡的垃圾的不同,走哪个格子并不考虑。因为要求要从1,1 到N * M ,并且只能往右或是下,这样的话沿途捡到的垃圾编号一定是递增的,这样问题就转换成在一系列垃圾中找最长的递增序列,但是这里仅仅递增还是不足够的,因为不能往左走,所以还得判断后面的那个垃圾的列是否有大于等于前一个垃圾的列。因为最终都要达到N * M,所以在这个点也手动加上个垃圾,如果这个点没有垃圾的话,就不要加上了。最后记录下选了第i个垃圾前面要选哪一个能够使得LIS最长。
dp【i】 = dp【k】 + 0 | 1(看这个位置是否有垃圾);
代码:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 105; const int maxn = 10010; int rec[N][N]; int f[maxn], num[maxn], s[maxn], y[maxn], d[maxn], p[maxn]; int n, m; void init (int& cnt) { cnt = 0; for (int i = 1; i <= n; i++)//将垃圾从小到大的排序 for (int j = 1; j <= m; j++) { if (rec[i][j] || (i == n && j == m)) { s[cnt] = (i - 1) * m + j; y[cnt] = j; d[cnt++] = rec[i][j]; } } } void printf_ans (int cnt) { if (cnt == -1) return; printf_ans(p[cnt]); if (d[cnt]) printf (" %d", s[cnt]); } int main () { int a, b, cnt, cas = 0; while (scanf ("%d%d", &n, &m) && n != -1 && m != -1) { memset (rec, 0, sizeof (rec)); while (scanf ("%d%d", &a, &b) , a && b) { rec[a][b] = 1; } init (cnt); for (int i = 0; i < cnt; i++) { f[i] = 0;//选择第i个垃圾能够捡到的最多的垃圾数目(还没有加上这个点垃圾数目) num[i] = 1;//选择第i个垃圾得到最多的垃圾的路线数目 p[i] = -1; } for (int i = 0; i < cnt; i++) { for (int j = i - 1; j >= 0; j--) {//为了保证字典序最小 if (y[j] <= y[i]) {//列坐标 if (f[j] > f[i]) { f[i] = f[j]; p[i] = j; num[i] = num[j]; } else if (f[j] == f[i]) { num[i] += num[j]; p[i] = j; } } } f[i] += d[i];//加上这点的垃圾数目 } printf ("CASE#%d: %d %d", ++cas, f[cnt - 1], num[cnt - 1]); printf_ans(cnt - 1); printf ("\n"); } return 0; }
时间: 2024-10-09 12:03:27