Description:
N≤200, Xi, Yi 的总和小于 2^31。
题解:
由于比较蠢,比赛时不知道怎么做。这种动态规划遇的很少,在这里总结一下。
先给出一种O(n^4)的动态规划。
枚举第一组第k大的是多少,设为h。
把这些选手以y为关键字从大到小排序。
设 fi,j,k f i , j , k 表示确定了前i个选手,放到一组且大于等于h的有j个,有k个放到第二组,的最大得分。
因为排了序,直接枚举当前的放哪就可以转移了。
让我们再贪心的想一想。
对于0的,如果它的 x<h x < h ,那么肯定是放在一组最优,因为不会造成影响。
对于1,如果 x<h x < h ,那就要放到二组去拼一拼。
由于0的 x<h x < h 对答案毫无影响,忽略掉它。
还是按y排序。
只设 fi,j f i , j 表示确定了前i个,放了j个到一组。
如果当前是0,可以放一组也可以放二组。
放一组的条件是 j<k j < k ,这个显然。
放二组的条件是 i−j<k i − j < k ,因为0的 x<h x < h 的已经被搞掉了,所以i-j就是在已经二组放了都少个。
1的同理。
可以总结出之所以降了一维是因为把0的 x<h x < h 去掉了,这样状态就可以表示出放了一组和二组分别多少个。
Code:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define max(a, b) ((a) > (b) ? (a) : (b))
using namespace std;
int n, k, m;
struct node{
int x, y, z;
} a[205], b[205];
int cmp(node a, node b) {return a.y > b.y;}
int f[205][205], ans;
int main() {
scanf("%d %d", &n, &k);
fo(i, 1, n) scanf("%d %d %d", &a[i].x, &a[i].y, &a[i].z);
fo(ii, 1, n) {
int h = a[ii].x;
m = 0;
fo(i, 1, n) if(a[i].z == 1 || (a[i].z == 0 && a[i].x >= h)) {
b[++ m] = a[i];
}
sort(b + 1, b + m + 1, cmp);
memset(f, 128, sizeof f);
if(b[1].x >= h) f[1][1] = b[1].z * b[1].x;
f[1][0] = b[1].y * b[1].z;
fo(i, 1, m - 1) {
fo(j, 0, i) {
if(b[i + 1].z == 0) {
if(j < k) f[i + 1][j + 1] = max(f[i + 1][j + 1], f[i][j]);
f[i + 1][j] = max(f[i + 1][j], f[i][j]);
} else {
if(b[i + 1].x < h){
if(i - j < k)
f[i + 1][j] = max(f[i + 1][j], f[i][j] + b[i + 1].y); else
f[i + 1][j] = max(f[i + 1][j], f[i][j]);
} else {
if(j < k)
f[i + 1][j + 1] = max(f[i + 1][j + 1], f[i][j] + b[i + 1].x);
if(i - j < k)
f[i + 1][j] = max(f[i + 1][j], f[i][j] + b[i + 1].y); else
f[i + 1][j] = max(f[i + 1][j], f[i][j]);
}
}
}
}
fo(j, 0, m) ans = max(ans, f[m][j]);
}
printf("%d", ans);
}