CF1730G

CF1703G

链接:

Problem - 1703G - Codeforces

题目大意:

你有 \(n\) 个箱子。第 \(i\) 个箱子中有 \(a_i\) 个硬币。你需要按照从箱子 \(1\) 号到箱子 \(n\) 号的顺序打开所有 \(n\) 个箱子。

你可以用以下两种钥匙之一打开一个箱子:

  • 好钥匙:使用一次消耗 \(k\) 个硬币。
  • 坏钥匙:使用时不消耗硬币,但会使所有未打开的箱子中的硬币数减半(包括正要打开的这个箱子)。硬币减半时向下取整。比如,用坏钥匙打开箱子 \(i\) 号时,\(a_i=a_i/2\)\(a_{i+1}=a_{i+1}/2\)\(......\)\(a_n=a_n/2\)

所有钥匙用过一次就会断掉(别想着买一把好钥匙开完所有箱子了),好钥匙需要重复付费,坏钥匙效果会重复计算。

也就是说,你总共需要使用 \(n\) 把钥匙,每个箱子用一把。开始时,你没有硬币和钥匙,如果想用好钥匙,你就得去买。值得注意的是,在这个过程中你可以赊账买钥匙;例如,如果你只有 \(1\) 个硬币,你也可以购买价值 \(k=3\) 个硬币的好钥匙,你的余额会变成 \(-2\) 个硬币。

你需要求出开完所有箱子之后你能获得的最大硬币数量(显然大于等于 \(0\) )。

思路:

做到这种题,我们一般有两个思路:

贪心orDP

我们首先应该考虑DP,因为DP基于暴力,更容易想;

这道题,我们会有一个除2操作,这个次数任意,这肯定是辅助我们的一个工具去用这个除2获取更大利益

题目要求最大硬币数量,我们就设 f(i) = 在i的位置的最大值硬币数

补充维度,一个维度i肯定不行,我们的工具使用多少次,这也需要一个维度,容量应该是log(1e9)大约开30即可

所以dp方程:

\( dp[i][j] = \begin{cases} dp[i - 1][j-1] + a[i]/2^j ,&j >0 \\ dp[i - 1][0] + a[i] , &j = 0 \end{cases} \)

代码:

const int N = 200005;
int n,k;
int a[N];
void solve(){
    cin >> n >> k;
    for(int i = 1;i<=n;i++) cin >> a[i];
    int dp[n + 1][31];
    int ma = 0;
    memset(dp,0,sizeof dp);
    for(int i = 1;i<=n;i++){
        int p = a[i];
        dp[i][0] = dp[i - 1][0] + a[i] - k;
        ma=max(dp[i][0],ma);
        for(int j = 1;j<=30;j++){
            p/=2;
            dp[i][j] = dp[i - 1][j - 1] + p;
            ma=max(dp[i][j],ma);
        }
    }
    cout << ma <<endl;
}

时间复杂度

O(30*n)

posted @ 2024-07-26 17:06  GsGXT  阅读(66)  评论(0)    收藏  举报