题目描述:
共有V张票,每一票只可能投给N个政党中的一个。共有M个席位。设编号为i的政党最终的得票为Vi,则议会中的席位按如下规则分配:
1、将得票数小于总选票的5%的政党剔除。
2、初始时议会为空,每个政党都只有0个席位。
3、对于每个政党P,计算一个参数Qp = Vp / (Sp + 1),Vp为政党P的最终得票,Sp为政党P当前已经在议会拥有的席位。
4、给Qp最大的政党分配一个席位,如果有多个政党的Qp相同,则将席位分给其中编号最小的政党。
5、重复3和4,直到议会已满。
由于计票还没有结束,现在我们只知道一部分选票的投票结果。给出V、N、M以及每个政党当前的得票,请你计算每个政党最多以及最少能赢得多少个席位。
N<=100,M<=200
题目分析:
经过一番思考之后觉得这个Qp飘忽不定不可捉摸不知所措然后被水淹没。
最大值很好解决,枚举每个人,把剩的所有票都给它然后暴力模拟,O(n2m)
最小值的话,二分当前 x x x号政党的席位数 m i d mid mid,然后求出剩下的政党得到 ≥ m − m i d \ge m-mid ≥m−mid个席位需要的最小票数,用 f [ i ] [ j ] f[i][j] f[i][j]表示前 i i i个政党得到 ≥ j \ge j ≥j个席位需要的最小票数,有 f [ i ] [ j ] = f [ i − 1 ] [ j − k ] + c o s t ( i , k ) f[i][j]=f[i-1][j-k]+cost(i,k) f[i][j]=f[i−1][j−k]+cost(i,k)
c
o
s
t
(
i
,
k
)
cost(i,k)
cost(i,k)表示让第
i
i
i个政党夺走
k
k
k个席位的最小需票数。
需要满足
- a [ x ] m i d + 1 < a [ i ] + c o s t ( i , k ) k + [ i < x ] \frac {a[x]}{mid+1}<\frac {a[i]+cost(i,k)}{k}+[i<x] mid+1a[x]<ka[i]+cost(i,k)+[i<x]
- a [ i ] + c o s t ( i , k ) ≥ 5 % V a[i]+cost(i,k)\ge5\%V a[i]+cost(i,k)≥5%V
为什么这么做是对的?
实际上第
i
i
i个政党最后不一定得到了
k
k
k个席位,但是可以确定的是,在
x
x
x政党将要取得mid+1个席位之时,
i
i
i政党一定取得到
k
k
k个席位。相当于用最小的票数构成一个确定的限制。并且在这个限制总数<m-mid的时候,
x
x
x政党一定可以取到第mid+1个席位。所以DP的正确性得以保证。
感性理解一波吧,这个题我想了2h+。。。
Code:
#include<bits/stdc++.h>
#define maxn 105
#define maxm 205
using namespace std;
int n,m,V,a[maxn],b[maxn],sum,f[maxm],ans[maxn],id[maxn];
void Maxsolve(){
for(int k=1;k<=n;k++){
a[k]+=V-sum,memset(b,0,(n+1)<<2);
for(int t=1,p;t<=m;b[p]++,t++){
for(p=1;p<=n;p++) if(a[p]*20>=V) break;
for(int i=p+1;i<=n;i++) if(a[i]*20>=V&&a[i]*(b[p]+1)>a[p]*(b[i]+1)) p=i;
}
a[k]-=V-sum,printf("%d%c",b[k],k==n?10:32);
}
}
bool cmp(int i,int j){return a[i]>a[j];}
bool cmp2(int i,int j){return i>j;}
bool check(int x,int mid){
memset(f,0x3f,sizeof f),f[0]=0;
for(int i=1;i<=n&&i<=20;i++) if(i!=x){
for(int j=m-mid;j>=1;j--)
for(int k=1;k<=j;k++){
int cst=max((a[x]*k%(mid+1)?a[x]*k/(mid+1)+1:a[x]*k/(mid+1)+(id[i]>id[x]))-a[i],0);
if((a[i]+cst)*20<V) cst=(V-1)/20+1-a[i];
f[j]=min(f[j],f[j-k]+cst);
}
}
return f[m-mid]<=V-sum;
}
int main()
{
scanf("%d%d%d",&V,&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum+=a[i];
Maxsolve();
for(int i=1;i<=n;i++) id[i]=i;
sort(id+1,id+1+n,cmp),sort(a+1,a+1+n,cmp2);
for(int i=1;i<=n;i++) if(a[i]*20>=V){
int l=0,r=m,mid;
while(l<r){
mid=(l+r)>>1;
if(check(i,mid)) r=mid;
else l=mid+1;
}
ans[id[i]]=l;
}
for(int i=1;i<=n;i++) printf("%d%c",ans[i],i==n?10:32);
}