看到数据,显然很可能是(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;
}