CCPC 2020 广告投放

题目链接
这是一个DP问题,状态表示: d p [ i ] [ j ] dp[i][j] dp[i][j]表示,前 i − 1 i-1 i1集都决策完毕后,到达第 i i i集且当前剩余观看人数为 j j j时,最大收益

很明显可以得到:
d p [ i + 1 ] [ j / d [ i ] ] = m a x ( d p [ i + 1 ] [ j / d [ i ] ] , d p [ i ] [ j ] + p [ i ] × j ) dp[i+1][j/d[i]]=max(dp[i+1][j/d[i]],dp[i][j]+p[i] \times j) dp[i+1][j/d[i]]=max(dp[i+1][j/d[i]],dp[i][j]+p[i]×j)
d p [ i + 1 ] [ j ] = d p [ i ] [ j ] dp[i+1][j]=dp[i][j] dp[i+1][j]=dp[i][j]

即由 d p [ i ] [ j ] dp[i][j] dp[i][j]这个状态可以推导出两个新的状态,分别是第 i + 1 i+1 i+1层,人数不变,即上一集没有插播广告,即没有收益,因此得到②式
i + 1 i+1 i+1层,人数发生变化(减少),即上一集中插播了广告,上一集由 j j j个人,因此插播广告后人数为 j / d [ i ] j/d[i] j/d[i],新获得的收益为 p [ i ] × j p[i] \times j p[i]×j,因此有①式

如果直接定义这么大一个二维数组会同时MLE和TLE。但其实没有必要,首先第 i + 1 i+1 i+1层的状态完全由第 i i i层推出,因此可以用滚动数组,递推方向从前向后,MLE解决了。但是两层循环遍历依然会TLE。是因为我们遍历了很多用不到的 j j j,地推过程中出现的 j j j的取值是 ⌊ m / a ⌋ \lfloor m/a \rfloor m/a ⌊ ⌊ m / a ⌋ / b ⌋ \lfloor\lfloor m/a\rfloor /b\rfloor m/a/b,而 ⌊ ⌊ m / a ⌋ / b ⌋ = ⌊ m / ( a × b ) ⌋ \lfloor\lfloor m/a\rfloor /b\rfloor =\lfloor m/(a\times b) \rfloor m/a/b=m/(a×b),也就是说,所有 j j j的取值都在 m m m的范围内,且数量不多,只需要预处理出来,就可以减少内层循环的次数。数量为根号级别,复杂度为 n m n\sqrt m nm
注意使用 long long

⌊ ⌊ m / a ⌋ / b ⌋ = ⌊ m / ( a × b ) ⌋ \lfloor\lfloor m/a\rfloor /b\rfloor =\lfloor m/(a\times b) \rfloor m/a/b=m/(a×b)简易证明:
⌊ m / ( a × b ) ⌋ \lfloor m/(a\times b) \rfloor m/(a×b)表示 m m m中含有多少个 a × b a\times b a×b
⌊ m / a ⌋ \lfloor m/a\rfloor m/a表示 m m m中含有多少个 a a a,设 ⌊ m / a ⌋ = l \lfloor m/a\rfloor=l m/a=l
m m m中包含 a × b a\times b a×b个数可表示为 ⌊ a × l a × b ⌋ \lfloor\frac{a\times l}{a\times b}\rfloor a×ba×l ⌊ l b ⌋ \lfloor\frac{l}{b}\rfloor bl(因为是 a × b a\times b a×b的倍数,则一定是 a a a的倍数,而 m m m以内 a a a的最大倍数是 a × l a\times l a×l,因此上限不写m而写成 a × l a\times l a×l)

#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;
}

猜你喜欢

转载自blog.csdn.net/weixin_43701790/article/details/110370495