多重背包问题(单调队列优化)

滑动窗口(引入)

 可以明确知道,暴力肯定会超时,现在引入单调队列来进行优化。

1.队列大小不超过k,这个由当前元素下标和队首存储下标差值计算,即  i - m + 1 > q[hh]

2.要保证队列内部元素是单调的,即 while (hh <= tt && a[i] <= a[q[tt]]) tt--;  while (hh <= tt && a[i] >= a[q[tt]]) tt--;  分别表示队首到队尾递增和递减

3.将当前元素在2的条件下存入队列

4.输出满足结果,先3后4

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define PII pair<int, int>
#define xx first
#define yy second
#define ULL unsigned long long
const int N = 1e6 + 10;
const int M = 1e3 + 10;
const int INF = 1e18;
const int ENF = -1e18;
const int mod = 1e9 + 7;
void ClearFloat()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
}
int read()
{
    int ret = 0, f = 1;
    char ch = getchar();
    while ('0' > ch || ch > '9')
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while ('0' <= ch && ch <= '9')
    {
        ret = ret * 10 + ch - '0';
        ch = getchar();
    }
    return ret * f;
}
int n, k, m, t, x, a[N], w[N], v[N], f[N], q[N];
int hh = 0, tt = -1;
bool st[N];
bool cmp(int a, int b)
{
    return a > b;
}
signed main()
{
    ClearFloat();
    cin >> n >> m;
    for (int i = 0; i < n; i++) cin >> a[i];
    for (int i = 0; i < n; i++)
    {
        if (hh <= tt && i - m + 1 > q[hh]) hh++;
        while (hh <= tt && a[i] <= a[q[tt]]) tt--;
        q[++tt] = i;
        if (i >= m - 1) cout << a[q[hh]] << ' ';
    }
    cout << endl;
    hh = 0, tt = -1;
    for (int i = 0; i < n; i++)
    {
        if (hh <= tt && i - m + 1 > q[hh]) hh++;
        while (hh <= tt && a[i] >= a[q[tt]]) tt--;
        q[++tt] = i;
        if (i >= m - 1) cout << a[q[hh]] << ' ';
    }
}

多重背包问题 (队列优化)

多重背包的原始状态转移方程:

f(i,j)=max(f(i−1,j),f(i−1,j−v)+w,⋯,f(i−1,j−sv)+sw)

f(i,j−v)=max(f(i−1,j−v),f(i−1,j−2v)+w,⋯,f(i−1,j−(s+1)v)+(s)w)

f(i,j−2v)=max(f(i−1,j−2v),f(i−1,j−3v)+w,⋯,f(i−1,j−(s+2)v)+sw)

...

此处我们取 r = j % v

f(i,r+sv)=max(f(i−1,r+sv),f(i−1,r+(s−1)v)+w,⋯,f(i−1,r)+sw)

f(i,r+2v)=max(f(i−1,r+2v),f(i−1,r+v)+w,f(i−1,r)+2w)

f(i,r+v)=max(f(i−1,r+v),f(i−1,r)+w)

f(i,r)=f(i−1,r)

此处引用铅笔佬的解析

 则可以利用滑动窗口在线性时间内求出答案

可以二维,但是内存卡的很紧

 二维朴素版

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define PII pair<int, int>
#define xx first
#define yy second
#define ULL unsigned long long
const int N = 1e6 + 10;
const int M = 1e3 + 10;
const int INF = 1e18;
const int ENF = -1e18;
const int mod = 1e9 + 7;
int n, m, w[1010], v[1010], f[1010][20010], s[1010], q[20010];
int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i] >> s[i];
    for(int i = 1; i <= n; i ++)
    {
        for(int r = 0; r < v[i]; r ++ )
        {
            int hh = 0, tt = -1;
            for(int j = r; j <= m; j += v[i])
            {
                while(hh <= tt && j - q[hh] > s[i] * v[i]) hh++;
                while(hh <= tt && f[i - 1][q[tt]] + (j - q[tt]) / v[i] * w[i] <= f[i - 1][j]) --tt;
                q[++tt] = j;
                f[i][j] = f[i - 1][q[hh]] + (j - q[hh]) / v[i] * w[i];
            }
        }
    }
    cout << f[n][m] << endl;
}

一维优化版

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define PII pair<int, int>
#define xx first
#define yy second
#define ULL unsigned long long
const int N = 20010;
const int M = 1e3 + 10;
const int INF = 1e18;
const int ENF = -1e18;
const int mod = 1e9 + 7;
void ClearFloat()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
}
int read()
{
    int ret = 0, f = 1;
    char ch = getchar();
    while ('0' > ch || ch > '9')
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while ('0' <= ch && ch <= '9')
    {
        ret = ret * 10 + ch - '0';
        ch = getchar();
    }
    return ret * f;
}
int n, m, t, x, v[N], w[N], s[N], f[N], g[N], q[N];
signed main()
{
    ClearFloat();
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) cin >> v[i] >> w[i] >> s[i];
    for (int i = 1; i <= n; ++i)
    {
        memcpy(g, f, sizeof g);//g存储f[i - 1][j]
        for (int r = 0; r < v[i]; r++)
        {
            int hh = 0, tt = -1;
            for (int j = r; j <= m; j += v[i])
            {
                while (hh <= tt && j - q[hh] > s[i] * v[i]) hh++;
                while (hh <= tt && g[q[tt]] + (j - q[tt]) / v[i] * w[i] <= g[j]) --tt;//f[i][j] = max(f[i][j], f[i - 1][j - s * v[i]] + s * w)
                q[++tt] = j;
                f[j] = g[q[hh]] + (j - q[hh]) / v[i] * w[i];
            }
        }
    }
    cout << f[m] << endl;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值