动态规划例题广告投放

题目描述

现在有一档综艺节目即将在网络上播出,总共会有 n 集,节目会按顺序逐集播出。节目组决定在某些集节目中投放广告。
节目最初播出时,会有 m 名观众观看。若第 i 集投放有广告,记此时还剩有 c 名观众观看,则会产生 c⋅pi 的收益;但播出后则会让观众的人数变为c′=⌊c/di⌋,即第 i+1 集只会剩有 c′ 名观众观看。如果在第 i 集没有投放广告,则不会产生收益,观众人数也不会变化。
请你帮助节目组计算一下各种可能的方案中,最大的收益和。

输入

第一行,两个整数 n 和 m (1≤n,m≤10^5),表示节目集数和首播时的观众数量。
第二行,共 n 个整数 pi (1≤pi≤10^6),表示第 i 集投放广告时每名观众能带来的收益。
第三行,共 n 个整数 di (1≤di≤m),表示第 i 集投放广告后,观众数量会变为人数除以 di 并向下取整。

输出

一个整数,表示可能的最大的收益和。

样例输入 Copy

5 20
9 14 10 4 5
2 7 1 8 10
样例输出 Copy

335
提示

样例输入二
6 5
5 31 53 58 74 97 5 5 4 5 5 4
样例输出二
485 第一个样例中,可以考虑在第 1, 2, 3, 5 集投放广告。这样,在第 1 集时有 2 名观众,投放广告获得收益 2;在第 2 集时有⌊20/2⌋=10 名观众,投放广告获得收益 10×14=140;在第 3 集时有 ⌊10/7⌋=1 名观众,投放广告获得收益 1×10=10;在第 4 集时有 ⌊1/1⌋=1 名观众,但因为没有投放广告,所以没有收益;在第 5 集时有 1 名观众,投放广告获得收益 1×5=5。最终,总收益为 1。这是能够取到最大的收益和的一个方案。
第二个样例中,可以考虑只在第 6 集投放广告,能获得的总收益为 5×97=485,这是能够取到最大的收益和的一个方案。 换个方案,如果选择在第 5, 6 集投放广告,能获得的总收益为 5×74+⌊5/5⌋×97=467,并不如只在第 6 集投放广告获得的总收益高。

一看这道题让求最大收益,又是有限制的选择,经典的DP问题,直接往这方面想了,然后想状态表示和转移,节目数和当前观众随着状态改变而改变,因此状态表示刚开始想的是f [i] [j] ,表示除了第i个节目当前剩余观众为j的最大收益。状态转移可表示为:
在这里插入图片描述
由于数据范围为1e5,这样的话n平方的复杂度绝对会超时的,我们会发现在第二层的遍历中便利了许多不可能出现的j,我们可以进行优化了。由于知道了初始的观众数和每次插入节目时使其产生变化的人数,我们可以预先处理,把可能出现的观众数储存下来,这样时间复杂度就降到了n根号m.这个需要牵扯到一个证明,可以看看大佬的证明和解释。
链接在此
这样时间复杂度降下来了,但是忽略了空间复杂度,当时也没想到,按照512MB的内存限制则 int类型的一维数组最多开1e7的大小,long long 类型的最多1e6,二维数组 int 最多开4000, long long 最多开1000,这我当时一算也真是没想到的,原来竞赛卡的这么严格。
那就看看怎样去进行空间优化,这时想到了状态压缩-------想到了经典的背包问题,这道题可不就是完全背包问题的模型吗? 这样瞬间有思路了,背包的体积相当于观众数,这样空间就优化到了一维。
完全背包问题
O的K ,现在开始敲代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int p[N], d[N];
typedef long long ll;
bool vis[N];
int a[N], ind;
int n, m;
ll dp[N];
// #define LOCAL
int main(){
#ifdef LOCAL
    freopen("in", "r", stdin);
    freopen("out", "w", stdout);
#endif
    scanf("%d %d", &n, &m);
    memset(vis, 0, sizeof vis);
    for (int i = 1; i <= n; i++)scanf("%d", p + i);
    for (int i = 1; i <= n; i++)scanf("%d", d + i);
    ind = 0;
    a[++ind] = 0;

    for (int i = 1; i <= m; i++){
        if (!vis[m / i]){
            vis[m / i] = 1;
            a[++ind] = m / i;
        }
    }
    sort(a + 1, a + 1 + ind);
    memset(dp, 0, sizeof dp);
    for (int i = 1; i <= n; i++){
        for (int j = 1; j <= ind; j++){
            dp[a[j] / d[i]] = max(dp[a[j] / d[i]], dp[a[j]] + (ll)p[i] * (ll)a[j]);
        }
    }
    ll ans = -1;
    for (int i = 0; i <= m; i++)ans = max(ans, dp[i]);
    cout << ans;
    return 0;
}

这题真是即使看出来是DP也难做出来啊,还得加油!!
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值