[背包DP] Vijos1412 多人背包(背包第k优解)(STL使用问题)

题目

VP1412
LP1858

思路

思路见背包九讲
刚开始考虑直接用stl的queue实现,vijos的第一个题解也是这么实现的,但是仔细一看好像 O(NVK) O ( N V K ) 好像不太行,然后vijosAC,洛谷一半的点TLE了。
后面手动用数组实现了一下队列,洛谷也AC了。
这里的问题就是STL的使用,对于复杂度不是很紧的题目,STL可以随便用。但是对于本题洛谷的数据,NVK乘起来就已经50000000的级别,再使用STL,就导致一半的点AC,另一半的还大多都是900ms+这样。
而使用数组实现以后,就真的是常数级别,最大的点也在300ms以下。
望注意
还有一些细节看代码

代码

STL队列

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#define _for(i,a,b) for(int i = (a); i<(b); i++)
#define _rep(i,a,b) for(int i = (a); i<=(b); i++)
using namespace std;

const int INF = 100000;
const int maxk = 50 + 10;
const int maxm = 5000 + 10;
queue<int> q1, q2;
int k, c, n, v, w, d[maxm][maxk];

int main() {
    scanf("%d%d%d", &k, &c, &n);

    _rep(i, 0, c) _for(j, 0, k) d[i][j] = -INF;  // 本题属于物体体积需要恰好等于背包容量的题,所以要初始化-INF
    d[0][0] = 0;

    _for(i, 0, n) {
        scanf("%d%d", &v, &w);
        for (int j = c; j >= v; j--) {
            // 合并两个队列 d[j]和d[j-v]+w,并选出前k个最优解
            _for(l, 0, k) {
                q1.push(d[j][l]);
                q2.push(d[j - v][l] + w);
            }
            _for(l, 0, k) {
                if (q1.front() > q2.front()) {
                    d[j][l] = q1.front();
                    q1.pop();
                }
                else {
                    d[j][l] = q2.front();
                    q2.pop();
                }
            }
            while (!q1.empty()) q1.pop();  // 队列没有自己的clear函数,所有要手动清空
            while (!q2.empty()) q2.pop();
        }
    }
    int ans = 0;
    _for(l, 0, k) if (k >= 0 ) ans += d[c][l];
    printf("%d\n", ans);

    return 0;
}

数组实现

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define _for(i,a,b) for(int i = (a); i<(b); i++)
#define _rep(i,a,b) for(int i = (a); i<=(b); i++)
using namespace std;

const int INF = 100000;
const int maxk = 50 + 10;
const int maxm = 5000 + 10;
int q1[maxk], q2[maxk];
int k, c, n, v, w, d[maxm][maxk];

int main() {
    scanf("%d%d%d", &k, &c, &n);

    _rep(i, 0, c) _for(j, 0, k) d[i][j] = -INF;  // 本题属于物体体积需要恰好等于背包容量的题,所以要初始化-INF
    d[0][0] = 0;

    _for(i, 0, n) {
        scanf("%d%d", &v, &w);
        for (int j = c; j >= v; j--) {
            // 合并两个队列 d[j]和d[j-v]+w,并选出前k个最优解
            _for(l, 0, k) {
                q1[l] = d[j][l];
                q2[l] = d[j - v][l] + w;
            }

            int len1 = 0, len2 = 0;
            _for(l, 0, k) {
                if (q1[len1] > q2[len2]) {
                    d[j][l] = q1[len1];
                    len1++;
                }
                else {
                    d[j][l] = q2[len2];
                    len2++;
                }
            }
        }
    }
    int ans = 0;
    _for(l, 0, k) if (k >= 0) ans += d[c][l];
    printf("%d\n", ans);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值