Google Kickstart2022 Round H Problem B 魔法百合井

很好的一道dp题
传送门

题意

森林里有一口很深的魔法井,井中有 L朵百合花。

你带着一个大空篮子和足够多的硬币来到了井边。

这个井有魔力,向里面投入硬币可以发生神奇的事情:

  • 如果你向井里一次性投入 1
    个硬币,井就会发动魔法,将一朵百合花扔进你的篮子里。

  • 如果你向井里一次性投入 4
    个硬币,井就会发动魔法,统计并记录到目前为止,已经扔进你的篮子里的百合花的数量。

  • 如果你向井里一次性投入 2
    个硬币,井就会发动魔法,将等同于上次记录数量的百合花扔进你的篮子里。

有一点需要特别注意,如果你向井里一次性投入 1个或 2个硬币后,井中已经没有足够的百合花扔给你了,那么井就不会发动任何魔法,也不会扔给你任何百合花(钱白花了)。

请你计算,为了将所有百合花都收入篮中,所需要花费的最少硬币数量

思考

  • 通过几次尝试,你会发现贪心貌似不可用

    贪心的思路,只统计目前已有的百合花,然后相加,你会发现,会留下一定数量的百合花,小于统计值,只能一个一个加,反而导致总硬币更多

  • 尝试dp怎么得到答案

    设f[x]是得到x朵花的最小硬币数

    我们先不考虑f[x]怎么得到
    考虑怎么得到之后
    假设相对应硬币操作,为1,2,4

    x之后 可能是421212122
    然而因为只统计了一次,所以2和1的顺序可以任意,也可以是422222111

    假设目前我们得到x朵花,花了f[x]个硬币,容易得到f[x+1]=f[x]+1
    若要再得到y-x朵花,我们也许可以通过f[y]=f[x]+y%x+y/x*2+4

通过将y分解为关于x的因式,状态转移方程更加简易,具体看代码

时间复杂度

对于每个i,循环n/i次,调和级数\(\sum_{i=1}^{i=n}\frac{1}{i}\)~\(lnn\)
O(\(\sum_{i=1}^{i=n}n/i\))=O(\(n*lnn\))=O(\(nlogn\))

CODE

#include<bits/stdc++.h>

using namespace std;
const int maxn=1e5+10;
int t;
int f[maxn];

int main(){
    memset(f,0x3f,sizeof(f));
    f[0]=0;
    for(int i=1;i<maxn;++i){
        f[i]=min(f[i],f[i-1]+1);
        for(int j=2;j*i<maxn;++j){
            f[i*j]=min(f[i*j],f[i]+4+2*(j-1));
        }
    }
    cin>>t;
    for(int i=1;i<=t;++i){
        int l;cin>>l;
        cout<<"Case #"<<i<<": "<<f[l]<<endl;
    }return 0;
    
}

感悟

最近经常使用数学归纳法求行列式,学习这个题的时候突然觉得,隐隐感觉与数学归纳法有所相似
目前所学的数学归纳法是从k推到k+1 ,但从此题可以看出,并非简单地从k推出,也可以出其他x<k推出

posted @ 2024-12-10 22:02  归游  阅读(25)  评论(0)    收藏  举报