• 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 
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
  • 相关阅读:
    关于离子色谱仪的结构和应用原理分析
    【求助】基于Ascend910的MindSpore训练无法复现GPU上的模型效果
    【云原生 • Kubernetes】kubernetes 核心技术 - Label 和 Selector
    云原生|kubernetes|kubernetes的网络插件calico和flannel安装以及切换
    Java网络编程
    【笔试强训选择题】Day35.习题(错题)解析
    学了C++能做什么?
    【JavaSe】断言 assert 到底怎么用?
    redis实现分布式锁的原理
    网络安全等级保护测评
  • 原文地址:https://blog.csdn.net/yingjiayu12/article/details/134441220