[CSP-S模拟测试]:毛三琛(随机化+二分答案)

题目传送门(内部题69)


输入格式

第一行正整数$n,P,k$。
第二行$n$个自然数$a_i$。$(0\leqslant a_i<P)$。


输出格式

仅一个数表示最重的背包的质量。


样例

样例输入:

5 5 2
0 4 2 1 3

样例输出:

5


数据范围与提示

样例解释:

取$x=3,a=\{3,2,0,4,1\}$。
分配方案为$\{3,2,0\},\{4,1\}$,质量最大的质量为$5$。

数据范围:

对于$20\%$的数据$n\leqslant 20,P\leqslant 50$。
对于$40\%$的数据$n\leqslant 1,000,P\leqslant 1,000$。
对于$100\%$的数据$n\leqslant 10,000,P\leqslant 10,000$。


题解

枚举$x$必不可少,计算答案可以用二分答案,时间复杂度是$\Theta(n\times P\log P)$的。

如果当前枚举的$x$无论如何也无法造成更优的贡献,则不去扫。

一个随机排列中比前面所有数都大的数的数量期望为$\log$。

然而这就是官方正解……

时间复杂度:$\Theta(n\times P+n\log n\log P)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
int n,P,K;
int a[10001],b[10001];
int ans=0x3f3f3f3f;
bool judge(int x)
{
	int res=0,sum=0;
	for(int i=1;i<=n;i++)
	{
		if(b[i]>x)return 0;
		if(res+b[i]>x)
		{
			res=0;
			sum++;
		}
		res+=b[i];
	}
	return sum<K;
}
int main()
{
	scanf("%d%d%d",&n,&P,&K);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=0;i<P;i++)
	{
		int lft=0,rht=0,res=10000;
		for(int j=1;j<=n;j++)
		{
			b[j]=(a[j]+i)%P;
			rht+=b[j];
		}
		if(!judge(ans))continue;
		while(lft<=rht)
		{
			int mid=(lft+rht)>>1;
			if(judge(mid)){res=mid;rht=mid-1;}
			else lft=mid+1;
		}
		ans=min(ans,res);
	}
	printf("%d",ans);
	return 0;
}

rp++

posted @ 2019-10-14 11:01  HEOI-动动  阅读(199)  评论(0编辑  收藏  举报