【GDKOI2015】星球杯

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 ,这个显然。

放二组的条件是 ij<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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值