POJ 1742 (单调队列优化多重背包+混合背包)

(点击此处查看原题)

题意分析

给你n种不同价值的硬币,价值为val[1],val[2]...val[n],每种价值的硬币有num[1],num[2]...num[n]个,问使用这n种硬币可以凑齐[1,m]内多少价值(换句话说,就是可以恰好支付的价格有多少)

解题思路

一开始觉得这个题也不是很难,就是多重背包问题,但是用二进制优化的多重背包写法TLE后,陷入了深思...

看了数据范围,二进制优化的时间复杂度为O(∑ log(num[i]  * V),加上多组输入后....应该是没被冤枉了.....

二进制都不行了,怎么办呢?只能去用单调队列优化多重背包问题了,其时间复杂度为O(n*V),时间刚刚好卡过去...

但是这里又出了一个问题,我用单调队列优化求解仍然得到了TLE?

QAQ,难道还有比单调队列更优的解法?借鉴大佬的题解后,发现这个题目严格意义上算是一个混合背包问题,而用单调队列处理完全背包问题的时候,效率很低,所以将这个题目按照混合背包分别处理后,就可以AC了。

(单调队列优化的讲解可以看看这个,讲的很清晰:http://www.cppblog.com/flyinghearts/archive/2010/09/01/125555.html

代码区

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<string>
#include<fstream>
#include<vector>
#include<stack>
#include <map>
#include <iomanip>

#define bug cout << "**********" << endl
#define show(x, y) cout<<"["<<x<<","<<y<<"] "
#define LOCAL = 1;
using namespace std;
typedef long long ll;
const int inf = 1e9 + 7;
const ll mod = 1e9 + 7;
const int Max = 1e5 + 10;

int n, v;
int que[Max], vol[Max], num[Max];
int head, tail;
bool dp[Max];

int main()
{
#ifdef LOCAL
    //    freopen("input.txt", "r", stdin);
    //    freopen("output.txt", "w", stdout);
#endif
    while (scanf("%d%d", &n, &v) != EOF && n + v)
    {
        for (int i = 1; i <= n; i++)
            scanf("%d", vol + i);            //将价值当作体积,无需计算最大价值
        for (int j = 1; j <= n; j++)
            scanf("%d", num + j);

        memset(dp, false, sizeof(dp));
        dp[0] = true;
        int cnt = 0;

        for (int i = 1; i <= n; i++)            //题目虽然没明说这个是混合背包,但是单调队列处理01背包和完全背包用时较长,
                                                //故将之用混合背包处理
        {
            if (num[i] == 1)                    //01背包
            {
                for (int j = v; j >= vol[i]; j--)
                    if (dp[j - vol[i]] && !dp[j])
                        dp[j] = true, cnt++;
                continue;
            }

            if (vol[i] * num[i] >= v)            //完全背包,这个比较关键,因为用单调队列处理非常耗时
            {
                for (int j = vol[i]; j <= v; j++)
                    if (dp[j - vol[i]] && !dp[j])
                        dp[j] = true, cnt++;
                continue;
            }

            for (int res = 0; res < vol[i]; res++)    //单调队列优化处理
            {
                head = 0, tail = -1;
                int sum = 0;
                for (int k = res; k <= v; k += vol[i])
                {
                    if (tail - head == num[i])
                        sum -= que[head++];
                    que[++tail] = dp[k];
                    sum += dp[k];
                    if (sum && !dp[k])
                        dp[k] = true, cnt++;
                }
            }
        }
        printf("%d\n", cnt);
    }
    return 0;
}
View Code
posted @ 2019-09-18 20:48  winter-bamboo  阅读(233)  评论(0编辑  收藏  举报