CF1539 D. PriceFixed

传送门:CF

前题提要:SB的题目假暗示

首先读完题目,不难发现会有一个贪心结论.对于现阶段能打折显然我们直接就买,因为对于现阶段已经能打折的物品来说,无论我们之后买多少物品,这个物品的价格都是不动的.但是我们当前买这个物品又能为后面的物品提供基数.所以对于状态为打折的物品显然我们越早买越好.

很好,现在的问题就来到了,我们当前没有能打折的物品,我们应该怎么办呢.贪心的去想,我们显然应该去买打折要求最高的物品,因为对于那些物品来说,他们打折的要求最高,就最不可能满足打折条件.但是所有的物品对打折都有贡献的.所以我们应该拿那些越难打折的物品去贡献那些越简单打折的物品,这样才是最优的

然后现在是最后一个问题了.只要你读完了题目,你就会发现题目中特意告诉你了,你可以去买那些已经打折的物品(即使他的需要已经满了),然后拿那些物品去让后面的物品打折.这个其实是一个大坑,因为你经过分析之后其实会发现这种操作一定是不优的.也就是我们根本不会使用这种操作.但是为什么呢?这部分贪心证明全网详细解释的博客很少(这也是本篇博客诞生的原因).

对于我们之前讨论出来的结论来说,我们最终的序列一定是 1 , 1 , 1....1 , 2 , 2 , 2 , . . . 2 , 1 , 1..1 , 2 , 2... , 1 , 1 1,1,1....1,2,2,2,...2,1,1..1,2,2...,1,1 1,1,1....1,2,2,2,...2,1,1..1,2,2...,1,1这样的,也就是连续的2后面接着1然后继续接着2.并且那些2的打折要求是递减的(也就是b数组的大小是递减的).那么此时我们给这个序列的头部加上了一个 1 1 1,此时会导致什么影响呢.你会发现这个最多(当然也可能没影响,分析方式一样,故此处不考虑没影响的情况)只会对我们序列中最后一个2产生影响.我们来详细分析一下为什么是这样的,对于我们新增的1,我们序列中已经打折的1显然是不受影响的,现在只需要考虑2.

可以用反证法来证明不可能有两个2同时受影响.如果有两个2同时受影响,那么就意味着这个两个2都只需要前面再来一个物品就步入打折状态了,那么位置在前面的那个2显然可以换到后面那个的位置,此时我们发现第一个2就变成了打折状态,此时才是最优的.与我们之前的序列最优产生了矛盾.

所以假设我们此时会新增一个打折的,那么显然是最后的那个2进入了打折行列.并且有且只有这个2,简单来说,造成的影响就是将最后一个2加入了它后面1的行列.所以也就是相当于只影响了一个物品从非打折变成了打折而已.你算一下此时的花费,发现是一样的.所以并不会产生影响.


下面是具体的代码部分:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
inline void print(__int128 x){
	if(x<0) {putchar('-');x=-x;}
	if(x>9) print(x/10);
	putchar(x%10+'0');
}
#define maxn 1000000
#define int long long
const double eps=1e-8;
#define	int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
struct Node{
	int a,b,id;
	bool operator < (const Node &rhs) const {
		return b<rhs.b;
	}
}node[maxn];
signed main() {
	int n=read();
	for(int i=1;i<=n;i++) {
		node[i].a=read();node[i].b=read();node[i].id=i;
	}
	sort(node+1,node+n+1);
	int l=1,r=n;int ans=0;int sum=0;
	while(l<=r) {
		while(l<=r&&node[l].b<=sum) {
			sum+=node[l].a;ans+=node[l].a;
			l++;
		}
		if(l>r) break;
		int temp=min(node[r].a,node[l].b-sum);
		node[r].a-=temp;
		sum+=temp;ans+=2*temp;
		if(node[r].a==0) r--;
	}
	cout<<ans<<endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值