1. 程式人生 > 其它 >CodeForces - 1395D Boboniu Chats with Du(貪心)

CodeForces - 1395D Boboniu Chats with Du(貪心)

技術標籤:貪心

題目連結:點選檢視

題目大意:給出一個長度為 n 的數列 a,現在可以對數列 a 進行重新排序,規定:

  1. 如果 a[ i] > m 的話,那麼接下來 d 天的數字不做貢獻
  2. 否則沒有影響

問如何排列可以使得貢獻和最大

題目分析:想到了是貪心,但一開始的思路是對整個數列排序,然後每次用最大的大於 m 的值去抵消最小的 d 個數,如此往復,不過需要考慮好多細節,而且貪心策略也無法證明是最優的,然後也沒實現出來

看了題解後才恍然大悟,其實可以將大於 m 的數和小於 m 的數分別儲存起來貪心排序儲存一下字首和,然後去列舉需要選擇多少個大於 m 的數即可,假設選了 x 個大於 m 個數,那麼需要佔用的位置:

  1. 最少是 ( x - 1 ) * ( d + 1 ) + 1,只需要將一個大於 m 的數放在末尾,剩下 x - 1 個數都佔用 d + 1 個位置
  2. 最多是 x * ( d + 1 ),即每個數都佔了 d + 1 個位置

那麼剩餘的空閒位置就是 n 減去佔用的位置了,空閒位置的最大值顯然是對應著上面的第一種情況,這些空閒位置都填上小於等於 m 的數即可

此時可能有人就有疑問了,假設剩下的空閒位置為 remain,remain 比小於等於 m 的數還要多怎麼辦呢?分兩種情況討論一下即可:(設 cnt 為小於等於 m 的數的個數)

  1. remain - cnt <= d:此時還需要 remain - cnt 個大於 m 的數補充序列,因為我們上面的情況 1 是將一個最大的數放在了末尾,只需要將這個數向前移動,就可以使得這 remain - cnt 個大於 m 的數不做貢獻
  2. remain - cnt > d:此時選擇的 x 一定不是最優的,因為顯然還可以再選擇一個 x,所以繼續迴圈下去一定是可以到達最優位置的

綜上所述,對於小於等於 m 的數,我們只需要選擇 min( cnt , remain ) 個即可

程式碼:

//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
using namespace std;
 
typedef long long LL;
 
typedef unsigned long long ull;

const int inf=0x3f3f3f3f;

const int N=1e6+100;

LL sum1[N],sum2[N];

int a[N],b[N],c1,c2;

int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.ans.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
	int n,d,m;
	scanf("%d%d%d",&n,&d,&m);
	for(int i=1;i<=n;i++)
	{
		int num;
		scanf("%d",&num);
		if(num>m)
			a[++c1]=num;
		else
			b[++c2]=num;
	}
	sort(a+1,a+1+c1,greater<int>());
	sort(b+1,b+1+c2,greater<int>());
	for(int i=1;i<=c1;i++)
		sum1[i]=sum1[i-1]+a[i];
	for(int i=1;i<=c2;i++)
		sum2[i]=sum2[i-1]+b[i];
	LL ans=sum2[c2];
	for(int i=1;i<=c1;i++)
	{
		int day=(i-1)*(d+1)+1;
		if(day>n)
			break;
		ans=max(ans,sum1[i]+sum2[min(n-day,c2)]);
	}
	printf("%lld\n",ans);










    return 0;
}