原题链接: https://codeforces.com/contest/1395/problem/D
题意: 你用过QQ吧,用过QQ就跳过咯?就是你有一堆有趣的快乐因子,然后你要搞怪群主,如果你的快乐因子大于群主的忍耐程度,那么你将会被禁言
天,问你在有限的
天中,你能达到的最大快乐因子总和。
解题思路: 这道题有点水,没错,确实有点水,你不信?听我的思路慢慢道来。我们有一堆快乐因子,有的是小于等于的,有的是大于等于
的。这两者一种会被禁言,一种不会被禁言,我们就把它们分成两组。再看我们的选择:我们可以选择什么?如果枚举完我们所有的选择情况,计算快乐因子总和,再取所有的情况最大值这道题就解决了。所以关键就是我们的选择有什么?
既然我们想要快乐因子总和最大,我们挑在不被禁言的组中肯定是挑快乐因子最大的,在被禁言的组中也肯定是挑快乐因子最大的。还有一个特殊的点就是我们在第天的时候可以选择被禁言达成最优效益,因为那个时候群主奈何不了你了,所以那个时候我们是不会被禁言的。所以我们设我们选择了
个快乐因子,那么会禁言
天,又因为使用每个快乐因子占用一天。所以我们总共花去了
天,所以我们用剩余的天数去使用不被禁言的快乐因子即可。OK,这样思路就十分清晰了。
我们的任务有:
- 对快乐因子分组,并从大到小排序(我们要最大肯定是要选最大的。)并用一个数组统计未被禁言的前缀和,这个用于我们之后求出的剩余天数
直接用下标就可以得出结果。
- 确定会被禁言的快乐因子的上限和下限用于
循环。通过
循环来枚举所有情况,得到最大值
。
- 值得注意的一点就是可能没有会被禁言的快乐因子,所以我们要让
的初值等于不被禁言的因子前缀最大和。
现在你不会觉得这题目难了吧,若还觉得难,看看代码吧,我贴了详细注释,加油。
AC代码:
/*
*
*
*/
//POJ不支持
//i为循环变量,a为初始值,n为界限值,递增
//i为循环变量, a为初始值,n为界限值,递减。
using namespace std;
const int inf = 0x3f3f3f3f;//无穷大
const int maxn = 1e5+5;//最大值。
typedef long long ll;
typedef long double ld;
typedef pair<ll, ll> pll;
typedef pair<int, int> pii;
//*******************************分割线,以上为自定义代码模板***************************************//
//所有的数据最好都用long long。我交了一发没用ll数据溢出了。
ll n,m,d;//n为天数,d为禁言天数,m为界限值。
ll a[maxn],b[maxn],pre[maxn],cnt1,cnt2;//pre数组统计未被禁言的前缀和,用于剩余天数的最大解。cnt1和cnt2统计他天数。
bool cmp(ll x,ll y){
//自定义比较函数作为sort的函数对象,也可以使用greater<int>().
return x>y;
}
int main(){
//freopen("in.txt", "r", stdin);//提交的时候要注释掉
IOS;
while(cin>>n>>d>>m){
cnt1=cnt2=0;
ll temp;
rep(i,1,n){
cin>>temp;
if(temp<=m)a[++cnt1]=temp; //统计不被禁言的快乐因子。
else b[++cnt2]=temp; //统计被禁言的快乐因子。
}
sort(a+1,a+1+cnt1,cmp);
sort(b+1,b+1+cnt2,cmp);
ll sum=0,ans=0,res;//sum统计被禁言的快乐因子总和,ans保存最优解,res存储剩余天数。
pre[0]=0;
rep(i,1,cnt1){
pre[i]=pre[i-1]+a[i];//求前缀和。
}
rep(i,cnt1+1,n){
pre[i]=pre[i-1];//未被禁言的快乐因子已经用完了,后面的也自然是如此等于最大的前缀和。
}
ans=pre[n];//提前统计好没有使用一天禁言天数的,因为禁言天数的因子可能没有。
rep(j,1,cnt2){
//j表示选取的禁言因子。
//接下来开始暴力枚举最优解。
sum+=b[j];
res=n-((j-1)*(d+1)+1);
if(res<0)break;//如果剩余天数小于0和大于不被禁言的因子数天数。自然就是失败的。
ans=max(ans,sum+pre[res]);
}
cout<<ans<<endl;
}
return 0;
}