cf1430F(dp/贪心好题)

cf1430F(dp/贪心好题)

题意:

现在有n波怪物,每波怪物起讫时间为l[i]~r[i],有a[i]只怪物,你现在有一把枪,弹夹容量为k,你开枪不用时间,一枪一个小朋友。但是换弹夹需要1s,并且换弹夹的时候,如果弹夹里有子弹,会将子弹扔掉再塞满。你需要在每波怪物的时间限制内将其杀光,并且花费的子弹数最少(包括扔掉的子弹),问你最少需要花费多少子弹。

**方法一:dp **

显然是求最少扔掉的子弹。我们考虑,在中间扔掉一定是不优的,需要扔掉的情况只有当后面一轮做不完的时候才扔,容易想到 d p [ i ] [ 0 / 1 ] dp[i][0/1] dp[i][0/1]表示前 i i i轮做完,第 i i i轮是扔还是不扔,但我们发现我们无法记录上一个扔掉的子弹数,太大了

但我们发现 d p [ i ] [ 0 / 1 ] = m i n ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] ) + ( c o s t ( 第 二 维 度 = = 1 ) ) dp[i][0/1]=min(dp[i-1][0],dp[i-1][1])+(cost(第二维度==1)) dp[i][0/1]=min(dp[i1][0],dp[i1][1])+(cost(==1))

即阶段从 i − 1 i-1 i1递推到 i i i可以化为由某个 j < i j<i j<i递推到 i i i,我们发现,换弹是关键点,使用 d p dp dp常用套路,枚举最后一个换弹的是哪个位置, d p [ i ] dp[i] dp[i]表示前 i i i个做完,第 i i i个做完后换弹的情况,枚举上一个最后换弹的地方,可以由某个 j j j转移过来,但是有两个需要注意的地方。

  • r i 和 l i + 1 r_i和l_{i+1} rili+1是可能有交点的,假如此时时间重叠,我们是不能换弹的,因为 d p [ j ] dp[j] dp[j]此时表示打完换了弹,用 d p [ j ] dp[j] dp[j]更新 d p [ j + 1 ] dp[j+1] dp[j+1]的时候,时间是不对的,少了1,使得后面的花费减少,此外这时候时间重叠不进行主动换弹显然是更优的,所以此时更新这个状态会导致后面花费错误的减少,不更新不会更劣,所以更新这个状态无意义,不更新
  • 可以先枚举 i i i,再枚举前面的 j j j,复杂度也是 O ( n 2 ) O(n^2) O(n2)的,但是写起来麻烦一点,这里直接枚举上一个换弹的地方依次向后转移写起来更方便。
#include<bits/stdc++.h>
using namespace std;
const int maxn=2005;
typedef long long ll;
ll dp[maxn];//前i个做完,第i个做完换了
int l[maxn],r[maxn],a[maxn],k,n;
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;++i)scanf("%d%d%d",&l[i],&r[i],&a[i]),dp[i]=1e18;
    dp[0]=0;
    for(int i=0;i<n;++i){
        ll ans=dp[i],num=k;
        for(int j=i+1;j<=n;++j){
            ll time=(max(0ll,(a[j]-num))+k-1)/k;
            if(l[j]+time>r[j])break;
            ll cost=num+time*k-a[j];
            ans+=a[j];
            if(j==n)dp[j]=min(dp[j],ans);
            else if(l[j]+time<l[j+1])//不更新这个状态
                dp[j]=min(dp[j],ans+cost);
            num=cost;
        }
    }
    cout<<(dp[n]==1e18?-1:dp[n])<<"\n";
    return 0;
}

启示:

1. d p [ i ] 由 d p [ i − 1 ] dp[i]由dp[i-1] dp[i]dp[i1]转移过来做不了的时候,可以观察由前面某个 j j j转移过来是否等价且能做

2.枚举最后一个xxx来划分 d p dp dp

3.形如 d p [ i ] = d p [ j ] + x x dp[i]=dp[j]+xx dp[i]=dp[j]+xx j < i j<i j<i的时候,可以尝试枚举前面顺序转移到后面

方法二:贪心(待补)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值