首页 > 代码库 > UVA - 10599Robots(II)(LIS)

UVA - 10599Robots(II)(LIS)

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



UVA - 10599Robots(II)(LIS)