21.11.12模拟 P3486 [POI2009]KON-Ticket Inspector

看到数据,显然很可能是(n^2k)的暴力DP,题目要求查询不同的人,就是个二维前缀和的过程。加入在i站查,上次查是j站,那么就是在j+1 ~ i上站,i+1 ~ n下站的人数,二维前缀和即可

注意,第i站是先下车再查询,

[f[i][k]表示第i站查第k次的最大价值, \ f[i][k]=max(f[j][k-1]+val), 0 le j < i ]

题目要求输出最小字典序,我们开个pre记录即可。记录能达到最大价值查k次的最小i,再往前找答案,这样的字典序是最小的

const int N = 607;
int f[N][57], pre[N][57]; 
int a[N][N];
int n, K, ans[N];

inline int sum(int x2, int y2, int x1, int y1) {
	return a[x2][y2] + a[x1-1][y1-1] - a[x1-1][y2] - a[x2][y1-1];
}
int main() {
	freopen("ticket.in", "r", stdin);
	freopen("ticket.out", "w", stdout);
	read(n);
	read(K);
	rep(i, 1, n) {
		rep(j, i+1, n) {
			read(a[i][j]);
		}
	}
	rep(i, 1, n) {
		rep(j, 1, n) {
			a[i][j] += a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1];
		}
	}

	memset(f, 0xcf, sizeof f);
	f[0][0] = 0;
	int mx(0),last(0);
	rep(k, 1, K) {
		rep(i, 1, n - 1) {
			rep(j, 0, i - 1) {
				int val = sum(i, n, j + 1, i + 1);
				if(f[i][k] < f[j][k - 1] + val) {
					f[i][k] = f[j][k - 1] + val;
					pre[i][k] = j;
				}
			}
			if(k == K && f[i][k] > mx) {
				mx = f[i][k];
				last= i;
			}
		}
	}
	int j(last);
	int t(K);
	do {
		ans[t] = j;
		j = pre[j][t];
	}while(t--);
	rep(i, 1, K) {
		out(ans[i], ' ');
	}
	putchar('
');
	return 0;
}

本文来自博客园,作者:{2519},转载请注明原文链接:https://www.cnblogs.com/QQ2519/p/15547297.html

原文地址:https://www.cnblogs.com/QQ2519/p/15547297.html