Codeforces Round #552 (Div. 3) F. Shovels Shop (前缀和预处理+贪心+dp)

题目:http://codeforces.com/contest/1154/problem/F

题意:给你n个商品,然后还有m个特价活动,你买满x件就把你当前的x件中最便宜的y件价格免费,问你买k件花最少的钱是多少

思路:首先这个特价活动就像点外卖一样满多少减多少,你可以分几次来使用同一个优惠或者不同的优惠,然后我们我们分析三个点

1那个特价活动中x>k的我们肯定都不会使用,因为那个时候已经买满了k件

2我们肯定是买最便宜的k件来使用特价活动

3我们其实可以当成分开几组来使用优惠,就像外卖有些时候要分几次来点才划得来一样

那么我们的思路就有了,我们dp[i]代表的是前i个物品的最高优惠价格,我们枚举最后一次使用特价活动是在哪个时候

为了方便我们用几个数组分别记录一些东西

s[i]    代表前i个的总花费

g[i]   代表买满i个最多能减少多少个物品值   (因为活动可能有重复,买i个减x个和y个,这个时候要取最优的情况)

dp[i] 代表的是前i个物品的最高优惠价格

我们枚举最后一次使用活动要枚举边界在哪,所以我们要使用二重循环枚举

这样转化一下,其实和我之前写的一篇数组分组是一样的思路了,主要还是要学会转化,建模

#include<bits/stdc++.h>
#define mod 1000007
#define maxn 200001
using namespace std;
typedef long long ll;
ll n,m,k;
ll dp[maxn];
ll s[maxn];
ll g[maxn];
ll a[maxn]; 
int main(){
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++) cin>>a[i];
    sort(a+1,a+n+1);
    for(int i=1;i<=k;i++){
        s[i]=s[i-1]+a[i];//记录前缀和
    }
    for(int i=1;i<=m;i++){
        ll x,y;
        cin>>x>>y;
        if(x>k) continue;
        g[x]=max(g[x],y); //保留特价活动的最优减少数
    }
    for(int i=1;i<=k;i++){  //由前推后
        for(int j=0;j<i;j++){//枚举最后一次的特价活动位置
            dp[i]=max(dp[i],dp[j]+s[j+g[i-j]]-s[j]);
        }
    }
    cout<<s[k]-dp[k];
} 

猜你喜欢

转载自www.cnblogs.com/Lis-/p/10724705.html