zoukankan      html  css  js  c++  java
  • Codeforces Round #713 (Div. 3) F. Education 超详细思考过程

    F. Education

    题意

    t组样例(t <= 1e4)
    
    每组样例给你n , c
    a[1] , a[2] ........ a[n] 
    b[1] , b[2] .........b[n-1]
    (n <= 2e5 , c <= 1e9)
    c表示目标的金钱
    
    如果你在等级i
    你每天可以赚a[i]的钱
    
    当然你在这一天也可以不赚钱
    升级你的等级
    
    如果你的等级在i
    升级到i+1级需要b[i]的钱
    
    你一开始在第1个等级
    问赚到至少c钱的最小天数
    
    数据保证 a递增
    a[1] <= a[2] <= a[3] <= ........ <= a[n]
    

    思路

    一看感觉是个dp
    
    每天都有2种选择
    要么赚钱,要么升级
    
    假设f[i][j]为当前在第i天,等级为j的情况下所赚的最大钱的数量
    考虑一下状态转移方程
    
    i这天赚钱
    f[i][j] = max( f[i][j],f[i-1][j] + a[j] )
    
    或者在i这天升级
    当然前提是f[i][j] >= b[j] 
    f[i][j] = max( f[i][j] , f[i-1][j-1] - b[j] )
    
    然后考虑一下时间复杂度
    O tn^2
    一定超时
    
    这。。。。。
    
    只能换一种思路
    那在考虑一下,一共最多n个等级
    假设只升到i这个等级
    所需要的最小花费
    如果可以 o1 算出来的话,我们可以枚举所有等级
    更新最小值
    
    

    那么
    设cost[i]数组表示只在等级i赚到c钱的最小天数

    所以cost[i] 等于 先升到i这个等级在赚够c的最小天数

    cost[1] = $lceil frac{c}{a[1]} ceil$

    cost[2] = $lceil frac{b[1]}{a[1]} ceil$ + $lceil frac{c}{a[2]} ceil$ + 1

    cost[3] = $lceil frac{b[1]}{a[1]} ceil$ + $lceil frac{b[2]}{a[2]} ceil$ + $lceil frac{c}{a[3]} ceil$ + 1 + 1

    cost[4] = $lceil frac{b[1]}{a[1]} ceil$ + $lceil frac{b[2]}{a[2]} ceil$ + $lceil frac{b[3]}{a[3]} ceil$ + $lceil frac{c}{a[3]} ceil$ + 1 + 1 + 1
    .......................

    cost[n] = $lceil frac{b[1]}{a[1]} ceil$ + $lceil frac{b[2]}{a[2]} ceil$ + $lceil frac{b[3]}{a[3]} ceil$ + ........ + $lceil frac{b[n-1]}{a[n-1]} ceil$ + $lceil frac{c}{a[n]} ceil$ + (n - 1)

    
    对上面这个式子稍微解释一下
    因为升级需要单独一天
    所以末尾要加上升级的天数
    
    
    这个式子还没有考虑到一个问题
    你升级的时候,假设现在已经赚了7元钱
    你升级需要6元钱
    那么你升级之后还剩下1元钱
    
    所以你还需要一个h[i] 数组表示
    升级到i这个等级所剩下的钱
    

    那么
    cost[1] = $lceil frac{c}{a[1]} ceil$

    cost[2] = $lceil frac{b[1]}{a[1]} ceil$ + $lceil frac{c-h[2]}{a[2]} ceil$ + 1

    cost[3] = $lceil frac{b[1]}{a[1]} ceil$ + $lceil frac{b[2]-h[2]}{a[2]} ceil$ + $lceil frac{c-h[3]}{a[3]} ceil$ + 1 + 1

    cost[4] = $lceil frac{b[1]}{a[1]} ceil$ + $lceil frac{b[2]-h[2]}{a[2]} ceil$ + $lceil frac{b[3]-h[3]}{a[3]} ceil$ + $lceil frac{c-h[4]}{a[3]} ceil$ + 1 + 1 + 1
    .......................

    cost[n] = $lceil frac{b[1]}{a[1]} ceil$ + $lceil frac{b[2]-h[2]}{a[2]} ceil$ + $lceil frac{b[3]-h[3]}{a[3]} ceil$ + ........ + $lceil frac{b[n-1]-h[n-1]}{a[n-1]} ceil$ + $lceil frac{c-h[n]}{a[n]} ceil$ + (n - 1)

    那么考虑一下怎么用o1的时间求出上面的式子
    
    我们可以发现前面的
    

    $lceil frac{b[1]}{a[1]} ceil$ + $lceil frac{b[2]-h[2]}{a[2]} ceil$ + $lceil frac{b[3]-h[3]}{a[3]} ceil$ + ........ + $lceil frac{b[n-1]-h[n-1]}{a[n-1]} ceil$

    可以用前缀和s[]数组来优化
    这样就可以做到 o1 时间来更新
    
    考虑一下加上s这个数组
    上面的式子变成了
    

    cost[1] = s[1] + $lceil frac{c}{a[1]} ceil$

    cost[2] = s[2] + $lceil frac{c-h[2]}{a[2]} ceil$ + 1

    cost[3] = s[3] + $lceil frac{c-h[3]}{a[3]} ceil$ + 1 + 1

    cost[4] = s[4] + $lceil frac{c-h[4]}{a[4]} ceil$ + 1 + 1 + 1
    .......................

    cost[n] = s[n] + $lceil frac{c-h[n]}{a[n]} ceil$ + (n - 1)

    注意一下c++里面取整默认为下取整
    所以
    $lceil frac{a}{b} ceil$ = $frac{a-b+1}{b}$

    所以最后的答案为 min($sum_{i=1}^{n}cost_i$)

    最终代码如下
    时间复杂度:O tn

    #include<bits/stdc++.h>
    #define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
    #define re register int
    #define pll pair<int,int> 
    #define x first 
    #define y second 
    #define sf(x) scanf("%d",&x)
    #define sfl(x) scanf("%lld",&x)
    typedef long long ll ;
    using namespace std;
    const int N =  1e6 + 10 , M = 1010 , inf = 0x3f3f3f3f , mod = 1e9 + 7 ;
    ll t , n , c ;
    ll a[N] , b[N] ; 
    ll s[N] ;  // s[i]数组表示前缀和的最小天数
    ll h[N] ;  // h[i]表示升级到i这个等级剩下的钱
    int main()
    {
        cin >> t ;
        
        while(t--)
        {
            cin >> n >> c ;
            
            fer(i,1,n) sfl(a[i]) ;
            
            fer(i,1,n-1) sfl(b[i]) ;
            
            s[1] = 0 , h[1] = 0 ;
            // s[1] , h[1] 初始化为0 ,方便后面计算
            ll have = 0 ; // have 表示上一轮剩下的钱
            fer(i,2,n)
            {
                if(have >= b[i-1])//如果上一轮剩下的钱足够升级,那直接升级
                {
                    s[i] = s[i-1] ;
                   	
                    have = have - b[i-1] ;
                    
                    h[i] = have ;
                }
                else 
                {
                    s[i] = s[i-1] + (b[i-1] - have + a[i-1] - 1) / a[i-1] ;
                    
                    if( (b[i-1] - have) % a[i-1] == 0) have = 0 ;
                    else have = a[i-1] - (b[i-1] - have) % a[i-1] ;
    				// 在草稿纸上推一下就可以得到have的表达式                
    
                    h[i] = have ;
                }
            }
            
            ll res = 1e18 ;
            
            fer(i,1,n)
            {
                res = min(res,s[i] + (c - h[i] + a[i] - 1) / a[i] + (i - 1)) ;
            }
            
            // fer(i,1,n) cout << h[i] << " " ;
            // cout << "
    " ;
            
            cout << res << "
    " ; 
        }
        return 0;
    }
    

    原创不易 点个赞在走呗

  • 相关阅读:
    c++ 学习笔记
    python 2048游戏控制器
    c++ 动态内存
    c++ 拷贝构造函数、拷贝运算符、析构函数
    c++ struct enum union加typedef与不加typedef
    c++ 动态内存2
    c++ 指针数组与指向数组的指针
    c++ TextQuery程序
    c++ virtual
    c++ 动态内存 动态数组
  • 原文地址:https://www.cnblogs.com/yueshehanjiang/p/14995456.html
Copyright © 2011-2022 走看看