[二分+DP]BZOJ1181 [CROATIAN2009]IZBROI选举 题解

版权声明:本文为博主原创文章,欢迎转载。 https://blog.csdn.net/try__jhf/article/details/82931931

题目大意

在地区选举中有 n n 个政党争夺 m m 个议会席位,总共有 V V 张票数,其中已经有一些票已经投出,议会席位的分配方式如下:设 v i v_i 为第 i i 个政党的票数,第 i i 个政党有 S i S_i 个席位,初始所有政党都没有席位,先把投票数小于 5 % 5\% 的政党剔除,每次把席位给 v i / ( S i + 1 ) v_i/(S_i+1) 最大的政党,问每个政党能得到的席位数的最大和最小值。

n 100 , m 200 n\le100,m\le200

解题分析

最大值很简单,把剩下所有的票都投给一个政党再模拟就行。

最小值可以把组按照当前票数 a i a_i 排序,可以考虑DP f [ i ] [ j ] f[i][j] 表示前 i i 组总共有 j j 个席位需要的最少票数(肯定尽量给比 i i 大的组 i i 而不会给比 x x 小的组),这样 f [ n ] [ m x ] < = s u m f[n][m-x]<=sum 中最小的 x x 就是 i i 组的答案。但是转移过程中会求至少需要多少票,这是只有得知了 i i 组最终有多少席位才能算出来的,所以我们需要先二分,然后用DP验证。

注意由于要剔除 5 % 5\% ,所以20个 5 % 5\% 就是 100 % 100\% 了,所以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;
}

猜你喜欢

转载自blog.csdn.net/try__jhf/article/details/82931931