codeforces1395D 贪心

思路清晰很重要,不然写出来的代码一堆的bug
题意:给你n个数,其中如果x>m,那么之后的d个数就没有作用,问你得到的和最多是多少
思路:一开始以为是dp,然而并不是,直接贪心就可以过了,对于a[i]>m的数放在一个数组里,其他数放在另一个数组里,从大到小排序,求前缀和,分别为a[i]和b[i],之后就是贪心的过程了,假设你可以用i个大于m的数,那么就意味着,你用去了day=(i-1)*(d+1)+1天,为什么这样写,因为首先可以想到把一个数放到最后,那么就是说你有一天用的单独的一天,但其他i-1用的是d+1天(包括自己本身),那么还有剩余的天数,也就是数组b可以用的天数,等于n-day?其实并不然,想想有一个bug,当你的单独成一天的那个位置也对这个数组起了作用的话,你用去的天数就不是(i-1) * (d+1)+1,还需要加上你没有在i-1里被覆盖的大于m的数被最后一个数覆盖了,这么一想好像还挺复杂的,所以说。。。思路要清晰。。
可以这么想,当你算的天数用少了的时候,x小于m的天数就会变多,可以肯定是大于数组b的长度的,所以在这里直接取一个min就可以了也就是min(n-day, lenb),就可以了
思路清晰的代码:

#define int ll
bool cmp(int x, int y){
	return x>y;
}
int a[maxn], b[maxn];
signed main(){
	int n=read(), d=read(), m=read();
	int l1=0, l2=0;
	rep(i,1,n){
		int x = read();
		if(x>m) a[++l1] = x;
		else b[++l2] = x;
	}	
	sort(a+1, a+l1+1, cmp);
	sort(b+1, b+l2+1, cmp);
	rep(i,1,l1) a[i] += a[i-1];
	rep(i,1,l2) b[i] += b[i-1];
	ll ans = b[l2];
	rep(i,1,l1){
		int day = (i-1)*(d+1)+1;
		if(day > n) break;
		ans = max(ans, a[i]+b[min(n-day, l2)]);
	}
	printf("%lld\n", ans);
	return 0;
}

思路不清晰的代码,调了好久的bug。。:

int a[maxn], b[maxn], c[maxn];
signed main(){
	// fopen;
	int n=read(), d=read(), m=read();
	rep(i,0,n-1) a[i]=read(); 
	sort(a, a+n);
	int x = n-(upper_bound(a, a+n, m)-a); //这一部分完全就是多余的,根本不需要这么麻烦,+1-1还让我想了有点久,直接在输入那里比较就行了,吐血
	rep(i,1,x-1) b[i] = b[i-1]+a[n-i-1];
	rep(i,1,n-x) c[i] = c[i-1]+a[n-x-i];
	/*rep(i,1,x-1) printf("b[%d]=%lld  ", i, b[i]);printf("\n");
	rep(i,1,n-x) printf("c[%d]=%lld  ", i, c[i]);printf("\n");*/
	if(!x){		//这里进行了以下特判其实可以不用,直接ans=c[n-x]就行
		printf("%lld\n", c[n]);return 0;
	}
	ll ans=0;
	n--;x--;
	int t1 = x/(d+1), t2= min(n/(d+1), x);	//这里是相对来说更严谨的地方,大于m的值取的个数肯定是有一个上限和下限的,但之前的代码中并没有,直接是1到lena,然后在循环中进行一下判断
	// printf("t1=%d t2=%d\n", t1, t2);
	rep(i,t1,t2){
		int cnt = n-1ll*i*(d+1);
		int tmp = c[min(cnt, n-x)]+b[i]+a[n];
		// printf("cnt=%lld tmp=%lld\n", cnt, tmp);
		ans = max(ans, tmp);
	}
	printf("%lld\n", ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值