[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++