题目描述
购物节快到了,思思想要购买一些商品。商场一共有n种商品出售,每一种商品的数量都是无限的。第i种商品单价初始为vi,每购买一件第𝑖i种商品可以给思思提供hi的快乐值。
思思希望在购物总花费不超过m的情况下,获得大于等于p的快乐值。然而,由于商品的初始单价较高,她可能无法在m的预算内达到获得大于等于p快乐值的目标。
幸运的是,思思得知商场接下来会依次发生t次促销活动。第j次促销活动包括三个参数lj,rj,wj,表示编号在lj到rj范围内的商品单价都减少wj。若一种商品单价减少wj后会小于等于0,那么这种商品的单价将变为1。也就是说,第𝑗j次促销活动的效果是让所有满足lj≤i≤rj的商品i,单价从vi更新为max(vi−wj,1)。每一次促销活动对商品单价的更新效果都会保留下来。
思思想要知道,她接下来最少需要等待多少个促销活动,可以使得降价促销后,她能在购物总花费不超过m的情况下,获得大于等于p的快乐值。如果思思不需要促销活动降价,在初始时就可以获得大于等于p快乐值,那么输出0。如果所有促销活动结束后,思思仍然实现不了目标,那么输出−1。
输入格式
输入的第一行,包括四个整数n,t,m,p。
接下来n行,每行两个整数vi,hi。
接下来t行,每行三个整数lj,rj,wj。
输出格式
输出一行,包括一个整数,表示思思最少需要等待多少个促销活动,才能在购物总花费不超过m的情况下,获得大于等于p的快乐值。如果所有促销活动结束后,思思仍然实现不了目标,那么输出−1。
样例输入 #1
1 5 8 41
5 10
1 1 1
1 1 1
1 1 1
1 1 1
1 1 1
样例输出 #1
4
样例输入 #2
1 5 8 100
5 10
1 1 1
1 1 1
1 1 1
1 1 1
1 1 1
样例输出 #2
-1
样例输入 #3
1 5 8 40
5 10
1 1 1
1 1 1
1 1 1
1 1 1
1 1 1
样例输出 #3
3
样例输入 #4
2 6 20 100
15 80
10 40
1 1 2
2 2 1
1 2 1
1 1 3
1 2 2
1 2 2
样例输出 #4
3
提示
本题共20个测试点,约定如下:
测试点 | n | t | vi,m |
---|---|---|---|
1 | 1 | ≤10 | ≤20 |
2 | 2 | ≤10 | ≤20 |
3 | 3 | ≤10 | ≤20 |
4 | 4 | ≤10 | ≤20 |
5 | 6 | ≤50 | ≤10^3 |
6 | 8 | ≤50 | ≤10^3 |
7 | 10 | ≤100 | ≤10^3 |
8 | 12 | ≤100 | ≤10^6 |
9 | 14 | ≤500 | ≤10^4 |
10 | 15 | ≤500 | ≤10^6 |
11 | 20 | ≤10^3 | ≤10^4 |
12 | 40 | ≤10^3 | ≤10^6 |
13 | 60 | ≤10^4 | ≤10^4 |
14 | 80 | ≤10^4 | ≤10^6 |
15 | 100 | ≤10^5 | ≤10^6 |
16 | 150 | ≤10^5 | ≤10^6 |
17 | 200 | ≤10^6 | ≤10^9 |
18 | 250 | ≤10^6 | ≤10^7 |
19 | 300 | ≤10^6 | ≤10^8 |
20 | 400 | ≤10^6 | ≤10^9 |
对于所有测试点均满足:1≤n≤400,1≤t≤10^6,1≤vi,m,wj≤10^9,1≤lj≤rj≤n,1≤hi,p≤10^3。
#include <bits/stdc++.h>
using namespace std;
long long d[410];//4e17
int n,t,m,p;//4e2 1e6 1e9 1e3
long long dp[400010];//懒得算了
long long inf=0x7f7f7f7f7f7f7f7fll;
int v[410],h[410],tmp[410];//1e9 1e3 1e9
int l[1000010],r[1000010],w[1000010];//4e2 4e2 1e9
bool check(int mid)
{
memset(d,0,sizeof(d));
for(int i=1;i<=mid;i++)
{
d[l[i]]+=w[i];
d[r[i]+1]-=w[i];
}
memcpy(tmp,v,sizeof(v));
for(int i=1;i<=n;i++)
{
d[i]+=d[i-1];
tmp[i]=max(1ll,tmp[i]-d[i]);
//cout<<tmp[i]<<endl;
}
memset(dp,0x7f,sizeof(dp));
dp[0]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=p;j++)
{
if(h[i]>j)
dp[j]=min(dp[j],1ll*tmp[i]);
else if(dp[j-h[i]]!=inf)
dp[j]=min(dp[j],dp[j-h[i]]+tmp[i]);
if(j>=p&&dp[j]<=m)
return true;
}
return false;
}
int bs(int l,int r)
{
while(l<r)
{
int mid=(l+r)/2;//1e6
if(check(mid))
r=mid;
else
l=mid+1;
}
return l;
}
int main()
{
cin>>n>>t>>m>>p;
for(int i=1;i<=n;i++)
scanf("%d %d",&v[i],&h[i]);
for(int i=1;i<=t;i++)
scanf("%d %d %d",&l[i],&r[i],&w[i]);
if(!check(t))
cout<<-1;
else
cout<<bs(0,t);
return 0;
}
前方高能!此乃一道T4难度的题,涉及二分,差分(含前缀和)和DP!
步骤、思路讲解:
从int main()看起。首先输入,如果你读题很仔细,你会发现这个输入量有点大,所以要使用scanf。千万不要小看这玩意,上回就是它给我多搞来了10分!
众所周知,如果思思不需要促销活动降价输出0,如果所有促销活动结束后仍然实现不了目标输出−1。-1的特判在输入下方,如果使用t个促销,即全部促销,都不能实现目标,输出-1。否则二分。二分时范围设为0~t,这样不需要促销的情况就也涵盖在里面了。
接着来到bs(binarySearch)函数。里面是二分答案最最最常见的写法,这里不做过多解释。这里使用的check和特判-1时功能相同,所以使用同一函数。
下面是本题的重头戏:check函数。先来个差分d数组(数据原因记得开long long),因为会多次使用先memset清零,再“降价”差分,此处是差分模板也不解释了。但是还是因为多次使用,多以开一个tmp,每次把v数组memcpy到tmp中结合差分计算。
差分完了,就该上DP了!正常思路是,dp[j]*代表用j元钱能买到的最大快乐值。可是价格的范围m高达10⁹,所以要换个思路:让dp[j]表示≥j快乐值是的最小钱数,因为快乐值的范围m只有10³!与普通完全背包的不同之处及注意事项如下:
1.存的是最小值,那么就要初始化成最大值,会涉及到long long,所以memset成0x7f7f7f7f7f7f7f7f。
2.dp[0]表示0快乐值需要的钱数,需初始化为0。
3.双层循环照常,里面的操作可分为三种情况(分支):
(1)j>h[i],说明j快乐值的这个任务你会超额完成,但也得完成呀,所以是min(dp[j],1ll*tmp[i])。
(2)dp[j-h[i]]是无穷大,说明这个任务肯定不能由i来完成了,不做改动。
(3)不属于任何一种特殊情况,直接用最常见的形式即可。
4.每次第一层循环的末尾都要看一下是否符合要求:快乐值>=p且需要最小钱数<=m,能的话直接return true(我不是这么写的但也可以)。
5.一直不符合,return false。
*:由于每次只与上一行有推导关系,可以将第一维删去。需要注意的是,这是完全背包,所以你不需要改成逆序。