购物

题目描述

购物节快到了,思思想要购买一些商品。商场一共有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个测试点,约定如下:

测试点ntvi​,m
11≤10≤20
22≤10≤20
33≤10≤20
44≤10≤20
56≤50≤10^3
68≤50≤10^3
710≤100≤10^3
812≤100≤10^6
914≤500≤10^4
1015≤500≤10^6
1120≤10^3≤10^4
1240≤10^3≤10^6
1360≤10^4≤10^4
1480≤10^4≤10^6
15100≤10^5≤10^6
16150≤10^5≤10^6
17200≤10^6≤10^9
18250≤10^6≤10^7
19300≤10^6≤10^8
20400≤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。

*:由于每次只与上一行有推导关系,可以将第一维删去。需要注意的是,这是完全背包,所以你不需要改成逆序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值