首页 > 其他分享 >反悔贪心

反悔贪心

时间:2023-08-05 12:55:53浏览次数:32  
标签:le 反悔 优惠券 最优 奶牛 贪心

贪心本身是没有反悔操作的,贪心求的就是当前的最优解。但当前的最优解有可能是局部最优解,而不是全局最优解,这时候就要进行反悔操作。

众所周知,正常的贪心算法是指在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解。也就是说我们的每一步都是站在当前产生的局面上所作出的最好的选择,是没有反悔操作的。
不加反悔的一直朝着当前局面的最优解走很可能导致我们被困在局部的最优解而无法到达全局的最优解,就好像我们爬山就只爬到了一座山的山顶却没有到整片山的最高处:
但是反悔贪心允许我们在发现现在不是全局最优解的情况下回退一步或若干步采取另外的策略去取得全局最优解。就好像我们站在的一座山的山峰上,看到了还有比我们现在所在位置还高的山峰,那我们现在就肯定不是在最高的地方了,这时我们就反悔——也就是下山再爬上那座更高的山:
这就是反悔贪心的大致思路。根据反悔记录操作的不同,反悔贪心又分为反悔堆和反悔自动机。
------------dalao

P3045 [USACO12FEB] Cow Coupons G

题目背景

Subtask 0 为原数据,Subtask 1 为 hack 数据。

题目描述

Farmer John needs new cows! There are N cows for sale (1 <= N <= 50,000), and FJ has to spend no more than his budget of M units of money (1 <= M <= 10^14). Cow i costs P_i money (1 <= P_i <= 10^9), but FJ has K coupons (1 <= K <= N), and when he uses a coupon on cow i, the cow costs C_i instead (1 <= C_i <= P_i). FJ can only use one coupon per cow, of course.

What is the maximum number of cows FJ can afford?

FJ 准备买一些新奶牛。市场上有 \(N\) 头奶牛,第 \(i\) 头奶牛价格为 \(P_i\)。FJ 有 \(K\) 张优惠券,使用优惠券购买第 \(i\) 头奶牛时价格会降为 \(C_i\),当然每头奶牛只能使用一次优惠券。FJ 想知道花不超过 \(M\) 的钱最多可以买多少奶牛?

  • \(1 \le K \le N \le 5 \times 10^4\)
  • \(1 \le C_i \le P_i \le 10^9\)
  • \(1 \le M \le 10^{14}\)

输入格式

* Line 1: Three space-separated integers: N, K, and M.

* Lines 2..N+1: Line i+1 contains two integers: P_i and C_i.

输出格式

* Line 1: A single integer, the maximum number of cows FJ can afford.

样例 #1

样例输入 #1

4 1 7 
3 2 
2 2 
8 1 
4 3

样例输出 #1

3

提示

FJ has 4 cows, 1 coupon, and a budget of 7.

FJ uses the coupon on cow 3 and buys cows 1, 2, and 3, for a total cost of 3 + 2 + 1 = 6.**

题目分析

这题一眼01背包,但是
\(1 \le K \le N \le 5 \times 10^4\)
\(1 \le M \le 10^{14}\)
显然不行所以考虑贪心,很直接的想法是将所有牛按c值排序买k个c值最小的,然后剩下的钱原价买。
所有钱只够买这前k只的情况很显然
以下考虑有剩余的钱买\(k+1\)到\(n\)的牛
这k个c值最小的一定会被买
反证法:如果有一种方案是这k只牛中有一只(A)不买而是买其他的(B),若A替换B来用优惠券
花的钱一定会更少,方案会更优
所以先将这k只牛用优惠券买了
再考虑调整
这k个c值最小的又不一定会用优惠券
这k个牛中所用的优惠券如果用在后面原价买的牛身上会使当前决策更优
即$ c_x+p_y > p_x+c_y(1<=x<=k,k+1<=y<=n)$
这时可以将这个优惠券转移,则这只牛买来的费用为 \(p_x-c_x+c_y\)
当然如果存在用原价买比用上面这种方式买更优那就直接用原价买。

代码实现

开三个小根堆:

  1. 前 k 只奶牛的 P 值与 C 值之差。

  2. 第 k + 1 到第 n 只奶牛的 C 值。

  3. 第 k + 1 到第 n 只奶牛的 P 值。

先按c值排序;
将前 k 只奶牛的 P 值与 C 值之差存入1堆;
将第 k + 1 到第 n 只奶牛的 C ,P值分别存入2,3堆;
每次按上面两种策略的最优策略买牛直到钱不够。

std

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef pair<int,int> PII;
const int N = 5e4+9;
int n,k;
ll m;
PII a[N];

bool vis[N];
ll sum;
int cnt;
priority_queue<int,vector<int>,greater<int> > d;//原价与优惠价的差值 
priority_queue<PII,vector<PII>,greater<PII> > p,c; 

int main()
{
	scanf("%d%d%lld",&n,&k,&m);
	for(int i = 1;i <= n;i++)scanf("%d%d",&a[i].second,&a[i].first);//sort排序pair<int,int>默认按first排 
	sort(a+1,a+1+n);
	for(int i = 1;i <= k;i++)
	{
		int t = a[i].second-a[i].first;
		d.push(t);
		sum += a[i].first;
		if(sum > m)printf("%d",i-1),exit(0);
	}
	if(sum > m)printf("%d",k-1),exit(0);
	
	cnt = k;
	for(int i = k+1;i <= n;i++)p.push({a[i].second,i}),c.push({a[i].first,i});
	
	while(sum < m)
	{
		//访问堆之前最好先判空
		while(!p.empty() && vis[p.top().second])p.pop();//已经买过的牛就不用再参与了 
		while(!c.empty() && vis[c.top().second])c.pop();
		
		if(c.empty() || p.empty())break;//买完了所有牛 
		
		PII tp = p.top(),tc = c.top();
		int td = d.top();//减少访问堆的次数节省时间 
		
		if(tp.first < tc.first + td)
		{
			if(sum + tp.first <= m)
			{
				cnt++;
				sum += tp.first;
				vis[tp.second] = 1;
				p.pop();
			}
			else break;//没钱了最便宜的都没不了 
		}
		else if(!d.empty())//判断还有没有优惠券 
		{
			if(sum + tc.first + td <= m)
			{
				cnt++;
				sum += tc.first + td;
				vis[tc.second] = 1;
				c.pop(),d.pop();
				d.push(a[tc.second].second-a[tc.second].first);
				//当前的决策只是局部最优之后优惠券还可能转移 
				//所以要将新的差值加入堆 
			}
			else break;//没钱了 
		}
		else //没优惠券了但还有钱,一直按原价买
		{
			while(!p.empty() && sum + p.top().first <= m)cnt++,sum += p.top().first,p.pop();
			break;
		}
	}
	
	printf("%d",cnt);
	
	return 0;
}
//--------AC7

标签:le,反悔,优惠券,最优,奶牛,贪心
From: https://www.cnblogs.com/AC7-/p/17607474.html

相关文章

  • 最长单调上升子序列(贪心+二分)
    这个的思路就是再开一个数组,存储长度为i的最长上升子序列的最后一个数字是多少,这个数组可以保证递增,之后开始二分,只要当前这个数是大于i-1的数但小于i的数,那就可以更新i的数,这里就是贪心的思想,相同长度结尾数字越小越好intlen=0;for(inti=1;i<=n;i++){intl=1,r=......
  • 贪心(不同情况下有不同策略)题单报告
    书接上回。感觉这个标题起得云里雾里的颇有上次讲的“反悔自动机”的奇妙风范,坏了会回旋镖插我自己身上了(感觉这样的贪心很厉害。什么叫不同情况下有不同策略呢?就是说你要分讨,分讨的每一种情况我们都要保证这是当前的最优解。这听起来是不是还是很扯,其实这是为了方便我自己看的......
  • 贪心(反悔贪心)题单报告
    Democy爷给了一份贪心的题单,但是由于我是小笨比,所以很多题我都不是很会做,现在来简单写一份总结,加强一下印象qwq。首先什么叫贪心?贪心就是我每次都选择一个最大值。比如说我现在有\(n\)个物品,每个物品都有一个价值,我们可以选\(k\)件物品,我们怎么样让选择的价值和最大呢?傻子......
  • 左神算法-基础06-前缀树&贪心算法
    左神算法-基础06-前缀树&贪心算法介绍前缀树何为前缀树?如何生成前缀树?例子:一个字符串类型的数组arr1,另一个字符串类型的数组arr2。arr2中有哪些字符,是arr1中出现的?请打印。arr2中有哪些字符,是作为arr1中某个字符串前缀出现的?请打印。arr2中有哪些字符,是作为arr1中某个......
  • 代码随想录贪心专题-day1
    35.分发糖果n个孩子站成一排。给你一个整数数组ratings表示每个孩子的评分。你需要按照以下要求,给这些孩子分发糖果:每个孩子至少分配到1个糖果。相邻两个孩子评分更高的孩子会获得更多的糖果。请你给每个孩子分发糖果,计算并返回需要准备的最少糖果数目。思路:本题这......
  • 2023夏季集训D1-贪心二分
    2023夏季集训D1贪心二分0x00前言24OIFXJ大佬来给我们讲课OrzOrz.讲课好难TAT.0x10贪心0x11经典贪心写了BestCowLineG/S和田忌赛马一位小伙从同学那里要来了一份BestCow代码Debug但没有发现代码没有输入,这是他思路3小时后发生的hack.田忌赛马太......
  • poj 1716 Integer Intervals (贪心)
    题意:给定n个连续的区间,求一个集合。其中,n个区间的每一个区间都至少包含两个这个集合的元素。求这个集合元素的最小值。 题解:1、先将所有区间按终点从小到大排序。2、我们先取第一个区间(排好序的区间)的两个值,因为要使得结果最优,我们肯定选择最接近终点的那两个值。假设一个为Selem,......
  • 【贪心】AGC018C Coins
    ProblemLink现在有\(X+Y+Z\)个人,第\(i\)个人有三个权值\(a_i,b_i,c_i\),现在要求依次选出\(X\)个人,\(Y\)个人和\(Z\)个人(一个人只能选依次),使得这\(X\)个人的\(a\)权值,\(Y\)个人的\(b\)权值,\(Z\)个人的\(c\)权值之和最大。\(X,Y,Z\le10^5\)。技巧:排序证明......
  • abc085d <贪心>
    题目D-KatanaThrower思路关键:连续使用ai与投掷bi并无冲突,可先使用ai再投掷bi找到ai中的最大值maxa;首先从大到小使用bi中比maxa大的元素,而后不足h再重复使用maxa(虽然按照先b后a的顺序分析计算,但实际上应是先用a后用b)代码Code//https://atcoder.jp/conte......
  • abc083d <思维 贪心>
    题目D-WideFlip思路参考live4m的博客其实全0和全1是无所谓的,只需要全部相同就行了,因为每次操作是令一个>=k区间的翻转,如果是全1,令[1,n]再翻转一次即可.考虑[1,i]已经相同,s[i]!=s[i+1]时如何操作,要使得[1,i+1]相同,要么[1,i]翻转,要么[i+1,n]翻转,为了使k最大,显......