版权声明:本文为博主原创文章,欢迎转载。 https://blog.csdn.net/try__jhf/article/details/82931931
题目大意
在地区选举中有 个政党争夺 个议会席位,总共有 张票数,其中已经有一些票已经投出,议会席位的分配方式如下:设 为第 个政党的票数,第 个政党有 个席位,初始所有政党都没有席位,先把投票数小于 的政党剔除,每次把席位给 最大的政党,问每个政党能得到的席位数的最大和最小值。
解题分析
最大值很简单,把剩下所有的票都投给一个政党再模拟就行。
最小值可以把组按照当前票数 排序,可以考虑DP 表示前 组总共有 个席位需要的最少票数(肯定尽量给比 大的组 而不会给比 小的组),这样 中最小的 就是 组的答案。但是转移过程中会求至少需要多少票,这是只有得知了 组最终有多少席位才能算出来的,所以我们需要先二分,然后用DP验证。
注意由于要剔除 ,所以20个 就是 了,所以DP枚举时最多只需枚举前20大的政党就行了。
示例代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int num,n,m,sum,a[205],b[205],ans[205],id[205],f[25][205];
inline int cmp(int x,int y){return a[x]>a[y];}
void _init(){
freopen("izeroi.in","r",stdin);
freopen("izeroi.out","w",stdout);
scanf("%d%d%d",&sum,&n,&m); num=sum;
for (int i=1;i<=n;i++){scanf("%d",&a[i]); sum-=a[i];}
}
void _solve0(){
for (int i=1;i<=n;i++){
a[i]+=sum; for (int j=1;j<=n;j++) b[j]=1;
for (int j=1,x=0,k;j<=m;j++,b[x]++,x=0){
for (k=1;k<=n;k++) if (a[k]*20>=num) {x=k; break;}
for (k++;k<=n;k++) if (a[k]*20>=num&&a[k]*b[x]>a[x]*b[k]) x=k;
}
ans[i]=b[i]-1; a[i]-=sum;
}
for (int i=1;i<=n;i++) printf("%d%c",ans[i],i!=n?' ':'\n');
}
bool _check(int x,int mid){
memset(f,63,sizeof(f)); f[0][0]=0;
for (int i=1;i<=n&&i<=20;i++){
if (i==x) {for (int j=0;j<=m;j++) f[i][j]=f[i-1][j]; continue;}
for (int j=0;j<=m;j++)
for (int k=0;k<=j;k++){
int tem=max((a[x]*k+mid)/(mid+1)-a[i]+(!(a[x]*k%(mid+1))&&k&&id[i]>id[x]),0);
if (k&&(tem+a[i])*20<num) tem+=(num-20*(tem+a[i])+19)/20;
f[i][j]=min(f[i][j],f[i-1][j-k]+tem);
}
}
return f[min(n,20)][m-mid]<=sum;
}
void _solve1(){
for (int i=1;i<=n;i++){b[i]=a[i]; id[i]=i;}
sort(id+1,id+n+1,cmp);
for (int i=1;i<=n;i++) a[i]=b[id[i]];
for (int i=1;i<=n;i++){
if (a[i]*20<num) {ans[id[i]]=0; continue;}
int L=0,R=m;
while (L<=R){
int mid=(L+R)>>1;
if (_check(i,mid)) R=mid-1; else L=mid+1;
}
ans[id[i]]=L;
}
for (int i=1;i<=n;i++) printf("%d%c",ans[i],i!=n?' ':'\n');
}
int main()
{
_init();
_solve0();
_solve1();
return 0;
}