前言
今天考试自闭了。
题目
思路
引例
首先,看到这道题就想到一道以前做过的贪心:不守交规
这道题的贪心思路是:先根据违约金大小排序,这是很容易想到的,然后呢?是按照时间从小到大排吗?并不是这样的,因为如果大件提前处理,可能就占据了处理小件的时间,但若是将其拖到最后交,就能够空余出交小件的时间。因此得到了贪心思路:尽量把大件拖到最后一天处理,如果这天在之前已经用过了,就在之前的天数里找空余,注意这里应该倒着找,因为越后面的天数要求越高,尽量空余出要求低的天数。若是没有空余则缴纳其费用。
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;
}

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

被折叠的 条评论
为什么被折叠?



