(5.23 A) [NOI2017]蔬菜 (贪心)

本文深入探讨了一种比赛算法策略,通过对比分析不同方法,提出了基于贪心算法的优化方案。通过对经典问题“不守交规”的解读,引出了对新问题的解决思路,即通过维护大根堆和小根堆来优化决策过程,同时利用并查集进行状态维护,以实现更高效的资源分配。文章详细阐述了代码实现细节,并附上了高效算法的示例代码。

前言

今天考试自闭了。

题目

洛谷

思路

引例

首先,看到这道题就想到一道以前做过的贪心:不守交规

在这里插入图片描述这道题的贪心思路是:先根据违约金大小排序,这是很容易想到的,然后呢?是按照时间从小到大排吗?并不是这样的,因为如果大件提前处理,可能就占据了处理小件的时间,但若是将其拖到最后交,就能够空余出交小件的时间。因此得到了贪心思路:尽量把大件拖到最后一天处理,如果这天在之前已经用过了,就在之前的天数里找空余,注意这里应该倒着找,因为越后面的天数要求越高,尽量空余出要求低的天数。若是没有空余则缴纳其费用。

Code:

	sort(Fuck + 1, Fuck + 1 + n);
	LL Wei = 0;
	for (Int i = 1; i <= n; ++ i)
	{
		bool Mst = 1;
		if (! Flag[Fuck[i].Til])
		{
			Flag[Fuck[i].Til] = 1;
			continue;
		}
		else
		{
			for (Int j = Fuck[i].Til - 1; j >= 1; -- j)
				if (! Flag[j])
				{
					Flag[j] = 1;
					Mst = 0;
					break;
				}
		}
		if ( Mst )
			Wei += Fuck[i].Mon;
	}
	printf("%lld", Wei);

本题

那么回到本题,该如何解决呢?

其实思路并不难懂。

首先,我们考虑如果是最大的 p p p 这一天,因为我们固定了这是 p p p 天的选取,我们可以确定这天那些蔬菜是没有变质的。那么我们只需要将蔬菜信息放入大根堆里面进行选取。进而,我们通过维护每个时候的小根堆,剔除多的元素。当然,有一些细节要注意,特别是当取 s s s值时,是否入队应该注意到。个人认为贪心思路和代码都不难,只要敢想敢打,注意细节,应该都能够过这道题。

话说回来,上面的思路和不守交规有什么关系呢?其实并没有什么关系,因为这是我赛后的做法,考试时的做法是:类似于不守交规,权值排序,会变质的拖到最后买——这需要将蔬菜拆成一个第一次和其余非第一次。但是不守交规里,我们是用枚举直接往前面找空余的位置,这道题显然不能这样搞,所以我们选择用一个并查集进行维护。附上 XJBG(信竞标杆) JZM的代码。

#include <cmath>
#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAXN 100005
#define LL long long
#define Int register int
using namespace std;
inline void read(LL &x)
{
	x = 0;
	LL f = 1;
	char s = getchar();
	while (s < '0' || s > '9')
	{
		if (s == '-')
			f = -1;
		s = getchar();
	}
	while (s >= '0' && s <= '9')
	{
		x = (x << 3) + (x << 1) + (s ^ 48);
		s = getchar();
	}
	x *= f;
}
inline LL Max(LL x,LL y)
{
	return x > y ? x : y;
}
inline LL Min(LL x,LL y)
{
	return x < y ? x : y;
}
struct Me
{
	LL Val, First, Store, Sour;
}Cai[MAXN];
struct Pai
{
	LL Key, Index;
	Pai(){}
	Pai(LL KEY,LL INDEX)
	{
		Key = KEY;
		Index = INDEX;
	}
	friend bool operator <(Pai A,Pai B)
	{
		return A.Key < B.Key;
	}
};
struct FPai
{
	LL Key, Index;
	FPai(){}
	FPai(LL KEY,LL INDEX)
	{
		Key = KEY;
		Index = INDEX;
	}
	friend bool operator <(FPai A,FPai B)
	{
		return A.Key > B.Key;
	}
};
queue<LL> Recycle;
priority_queue<Pai> Q;
priority_queue<FPai> Qt;
LL Ans[MAXN], Til[MAXN], Maxx;
vector<LL> Die[MAXN];
LL Before[MAXN];
int main()
{
	LL n, m, k;
	read( n ); read( m ); read( k );
	for (Int i = 1; i <= n; ++ i)
	{
		read( Cai[i].Val );
		read( Cai[i].First );
		read( Cai[i].Store );
		read( Cai[i].Sour );
	}
	for (Int i = 1; i <= k; ++ i)
	{
		read( Til[i] );
		Maxx = Max(Maxx, Til[i]);
	}
	for (Int i = 1; i <= n; ++ i)
	{
		if (Cai[i].Sour == 0)
			Die[Maxx].push_back( i ) ;
		else Die[Min(Maxx, (Cai[i].Store + Cai[i].Sour - 1) / Cai[i].Sour)].push_back( i ); 
	}
	for (Int i = Maxx; i >= 1; -- i)
	{
		int l1 = Die[i].size();
		for (Int j = 0; j < l1; ++ j)
			Q.push(Pai(Cai[Die[i][j]].Val + Cai[Die[i][j]].First, Die[i][j]));
		LL Temp = m;
		while (! Q.empty() && Temp)
		{
			Pai Now = Q.top();
			Q.pop();
			if (! Before[Now.Index])
			{
				Before[Now.Index] ++;
				Ans[Maxx] += Now.Key; Temp --;
				if (Cai[Now.Index].Store != 1)
					Q.push(Pai(Cai[Now.Index].Val, Now.Index));
			}
			else
			{
				LL Sheng = Cai[Now.Index].Store - Before[Now.Index] - (i - 1) * Cai[Now.Index].Sour;
				LL Sell = Min(Temp, Sheng);
				Temp -= Sell;
				Before[Now.Index] += Sell;
				Ans[Maxx] += Sell * Now.Key;
				if (Cai[Now.Index].Store != Before[Now.Index])
					Recycle.push( Now.Index );
			}
		}
		while (! Recycle.empty())
		{
			LL Cha = Recycle.front();
			Recycle.pop();
			Q.push(Pai(Cai[Cha].Val, Cha));
		}
		//printf("i = %d, Ans = %lld\n",i,Ans[Maxx]);
	}
	//printf("PPLXJBG %lld\n", Ans[Maxx]);
	LL All = 0;
	for (Int i = 1; i <= n; ++ i)
	{
		if (Before[i] == 1)
			Qt.push(FPai(Cai[i].Val + Cai[i].First, i));
		else if (Before[i] != 0)
			Qt.push(FPai(Cai[i].Val, i));
		All += Before[i];
	}
	for (Int i = Maxx - 1; i >= 1; -- i)
	{
		LL Liter = All - i * m;
		Ans[i] = Ans[i + 1];
		if (Liter <= 0)
			continue;
		while (! Qt.empty() && Liter)
		{
			FPai Now = Qt.top();
			Qt.pop();
			if (Before[Now.Index] == 1)
			{
				Liter --;
				Ans[i] -= Now.Key;
				Before[Now.Index] = 0;
			}
			else
			{
				LL Lite = Min(Liter, Before[Now.Index] - 1);
				Liter -= Lite;
				Before[Now.Index] -= Lite;
				Ans[i] -= Lite * Now.Key;
				if (Before[Now.Index] == 1)
					Qt.push(FPai(Cai[Now.Index].Val + Cai[Now.Index].First, Now.Index));
				else Qt.push(FPai(Cai[Now.Index].Val, Now.Index));
			}
		}
		All = i * m;
	}
	for (Int i = 1; i <= k; ++ i)
		printf("%lld\n", Ans[Til[i]]);
	return 0;
}

JZM’s

#include <cstdio>
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
typedef long long int_;
typedef pair<int,int> PII;
inline int readint(){
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MaxN = 100005;
priority_queue< PII > pq;
int x[MaxN], c[MaxN];
int s[MaxN], a[MaxN];
int sold[MaxN]; // 每种蔬菜卖了几个
// 第d天时还剩几个
int left(int i,int d){
	if(x[i] == 0) return c[i]-sold[i];
	if((c[i]-sold[i])/x[i] < d-1)
		return 0;
	return c[i]-sold[i]-x[i]*(d-1);
}
int_ ans[MaxN]; // 答案
// 有哪些蔬菜从这一天开始出现
vector< int > appear[MaxN];
vector< int > v; // 暂时删除

int main(){
	int n = readint(), m = readint();
	int k = readint();
	const int MaxP = MaxN-5;
	for(int i=1; i<=n; ++i){
		a[i] = readint();
		s[i] = readint();
		c[i] = readint();
		x[i] = readint();
		if(x[i] != 0){
			int t = (c[i]+x[i]-1)/x[i];
			t = min(t,MaxP); // 不需要更多
			appear[t].push_back(i);
		}
		else pq.push(make_pair(s[i]+a[i],i));
	}

	int_ &res = ans[MaxP];
	for(int i=MaxP; i; --i){
		for(auto t : appear[i])
			pq.push(make_pair(s[t]+a[t],t));
		v.clear();
		for(int now=m; now; ){
			if(pq.empty()) break;
			int t = pq.top().second;
			pq.pop(); // 弹出t
			int cnt = min(left(t,i),now);
			if(sold[t] == 0){
				res += s[t]+a[t];
				-- now, ++ sold[t];
				if(left(t,i))
					pq.push(make_pair(a[t],t));
				else v.push_back(t);
			}
			else{
				res += 1ll*cnt*a[t];
				now -= cnt, sold[t] += cnt;
				v.push_back(t);
			}
		}
		for(int j=0,len=v.size(); j<len; ++j){
			int t = v[j];
			if(sold[t] != c[t])
				pq.push(make_pair(a[t],t));
		}
	}

	while(!pq.empty()) pq.pop();
	int all = 0; // 一共卖了多少个
	for(int i=1; i<=n; ++i)
		all += sold[i];
	for(int i=1; i<=n; ++i)
		if(sold[i] > 1)
			pq.push(make_pair(-a[i],i));
		else if(sold[i] == 1)
			pq.push(make_pair(-a[i]-s[i],i));
	for(int i=MaxP-1; i; --i){
		ans[i] = ans[i+1];
		for(; all>m*i; ){
			int t = pq.top().second;
			pq.pop(); int now = all-m*i;
			int cnt = min(sold[t]-1,now);
			if(sold[t] == 1){
				ans[i] -= (s[t]+a[t]);
				-- sold[t], -- all;
			}
			else{
				ans[i] -= 1ll*cnt*a[t];
				sold[t] -= cnt, all -= cnt;
				if(sold[t] == 1)
					pq.push(make_pair(-s[t]-a[t],t));
				else pq.push(make_pair(-a[t],t));
			}
		}
	}

	for(; k; --k)
		printf("%lld\n",ans[readint()]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值