AcWing
  • 首页
  • 题库
  • 题解
  • 分享
  • 问答
  • 活动
  • 应用
  • 吐槽
  • 登录/注册

关于多重背包的最终优化



1


本问题以C++为准

多重背包大家都很熟悉吧

它的优化从低到高如下

  • 朴素 - 多重背包I
  • 二进制 - 多重背包II
  • 单调队列 - 多重背包III

显然,二进制优化的代码是跑不过 多重背包III 的

但是我们可以在代码前面开挂:

#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")

二进制优化版本加上挂之后平均跑 372ms
单调队列优化版本平均约跑 1499ms
单调队列优化版本加上挂之后平均约跑 860ms
注:以上均为3次测验的平均

可以明显看出,二进制版本加上挂比单调队列快很多
但是二进制版本不加挂是跑不过去的,单调队列却可以

???

所以事实证明算法的优劣不决定成败,高铁头才是王道

望解答开挂单调队列不如开挂二进制的原因

送关注一个

附录:

以下代码均由 yxc 大佬提供

单调队列优化代码


#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 20010;

int n, m;
int f[N], g[N], q[N];

int main()
{
    cin >> n >> m;
    for (int i = 0; i < n; i ++ )
    {
        int v, w, s;
        cin >> v >> w >> s;
        memcpy(g, f, sizeof f);
        for (int j = 0; j < v; j ++ )
        {
            int hh = 0, tt = -1;
            for (int k = j; k <= m; k += v)
            {
                if (hh <= tt && q[hh] < k - s * v) hh ++ ;
                while (hh <= tt && g[q[tt]] - (q[tt] - j) / v * w <= g[k] - (k - j) / v * w) tt -- ;
                q[ ++ tt] = k;
                f[k] = g[q[hh]] + (k - q[hh]) / v * w;
            }
        }
    }

    cout << f[m] << endl;

    return 0;
}

二进制优化代码

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010, M = 20010;

int n, m;
int v[N], w[N];
int f[M];

int main()
{
    cin >> n >> m;

    int cnt = 0;
    for (int i = 1; i <= n; i ++ )
    {
        int a, b, s;
        cin >> a >> b >> s;
        int k = 1;
        while (k <= s)
        {
            cnt ++ ;
            v[cnt] = a * k;
            w[cnt] = b * k;
            s -= k;
            k *= 2;
        }
        if (s > 0)
        {
            cnt ++ ;
            v[cnt] = a * s;
            w[cnt] = b * s;
        }
    }

    n = cnt;

    for (int i = 1; i <= n; i ++ )
        for (int j = m; j >= v[i]; j -- )
            f[j] = max(f[j], f[j - v[i]] + w[i]);

    cout << f[m] << endl;

    return 0;
}

臭氧:

#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
这个是从cht的打卡里拿来的
dp,多重背包

提问于3天前
wx090708
9817

盲猜是 memcpy 比较慢?你可以试试看( 另外问题无关,用 cin cout 最好关闭同步流,不然很慢( –  RingweEH   3天前

@RingweEH:  都是cin cout 应该没问题,memcpy我把它改成滚动数组还是慢( –  wx090708   3天前

@wx090708:  可能和单调队列里面的除法比较多有关,新的 C++ 好像是已经乘法和加法差不多复杂度了,但是除法和取模还是比较慢,可能是这个原因。(虽然我觉得不至于差一倍,但是可能循环多了有关吧)或许可以百度“O2优化原理” 看看每个优化项的作用分析一下(( –  RingweEH   3天前

@RingweEH:  我试试 –  wx090708   3天前



2 个问答



1

#include <iostream>
#include <algorithm>
#include <cstring>
const int N = 100010, M = 20010;

int n, m;
int v[N], w[N];

inline void umax(int& a,int t){if(t>a)a=t;}
int pre[M],nxt[M];
int main()
{
    scanf("%d%d",&n,&m);
    int cnt = 0;
    for (int i = 1; i <= n; i ++ )
    {
        int a, b, s;
        scanf("%d%d%d",&a,&b,&s);
        int k = 1;
        while (k <= s)
        {
            cnt ++ ;
            v[cnt] = a * k;
            w[cnt] = b * k;
            s -= k;
            k *= 2;
        }
        if (s > 0)
        {
            cnt ++ ;
            v[cnt] = a * s;
            w[cnt] = b * s;
        }
    }

    n = cnt;

    for (int i = 1; i <= n; i ++ )
    {
        memcpy(pre,nxt,sizeof pre);
        int v=::v[i],w=::w[i];
        int j=v;
        for(;j+3<=m;j+=4)
            umax(nxt[j],pre[j-v]+w),
            umax(nxt[j+1],pre[j+1-v]+w),
            umax(nxt[j+2],pre[j+2-v]+w),
            umax(nxt[j+3],pre[j+3-v]+w);
        for(;j<=m;++j)
            umax(nxt[j],pre[j-v]+w);
    }
    printf("%d\n",nxt[m]);

    return 0;
}

回答于16小时前
whsstory
62489

看着是不是说把循环枚举改成一小段一小段地实现 –  wx090708   11小时前

%%%万弘大佬tql –  wx090708   11小时前



1

Ofast会把一部分循环给循环展开,而且二进制分组之后背包的内部循环寻址连续,循环展开可以取得很好的效果(几乎可以认为是除以4);
但单调队列寻址显然不连续并且用到了除法

回答于17小时前
whsstory
62489

@wx090708:  我刚才手动展开了一下,现在不用优化开关也能过(但是我写的比编译器慢不少) –  whsstory   16小时前

感谢大佬! –  wx090708   17小时前


我来回答
你确定删除吗?

© 2018-2021 AcWing 版权所有  |  京ICP备17053197号-1
联系我们  |  常见问题
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标
请输入绑定的邮箱地址
请输入注册信息