题目: 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;
}