CodeForces - 1395D Boboniu Chats with Du(貪心)
阿新 • • 發佈:2020-12-10
技術標籤:貪心
題目連結:點選檢視
題目大意:給出一個長度為 n 的數列 a,現在可以對數列 a 進行重新排序,規定:
- 如果 a[ i] > m 的話,那麼接下來 d 天的數字不做貢獻
- 否則沒有影響
問如何排列可以使得貢獻和最大
題目分析:想到了是貪心,但一開始的思路是對整個數列排序,然後每次用最大的大於 m 的值去抵消最小的 d 個數,如此往復,不過需要考慮好多細節,而且貪心策略也無法證明是最優的,然後也沒實現出來
看了題解後才恍然大悟,其實可以將大於 m 的數和小於 m 的數分別儲存起來貪心排序儲存一下字首和,然後去列舉需要選擇多少個大於 m 的數即可,假設選了 x 個大於 m 個數,那麼需要佔用的位置:
- 最少是 ( x - 1 ) * ( d + 1 ) + 1,只需要將一個大於 m 的數放在末尾,剩下 x - 1 個數都佔用 d + 1 個位置
- 最多是 x * ( d + 1 ),即每個數都佔了 d + 1 個位置
那麼剩餘的空閒位置就是 n 減去佔用的位置了,空閒位置的最大值顯然是對應著上面的第一種情況,這些空閒位置都填上小於等於 m 的數即可
此時可能有人就有疑問了,假設剩下的空閒位置為 remain,remain 比小於等於 m 的數還要多怎麼辦呢?分兩種情況討論一下即可:(設 cnt 為小於等於 m 的數的個數)
- remain - cnt <= d:此時還需要 remain - cnt 個大於 m 的數補充序列,因為我們上面的情況 1 是將一個最大的數放在了末尾,只需要將這個數向前移動,就可以使得這 remain - cnt 個大於 m 的數不做貢獻
- 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; }