• 【BZOJ1181】[CROATIAN2009] IZBROI选举(贪心+模拟&&二分+DP)


    点此看题面

    大致题意: 共有(V)张选票,有(n)个政党竞争(m)个席位。设第(i)个政党有(V_i)张选票,已获得(S_i)个席位,则每个席位将会分给获得选票数量大于等于总选票数(5\%)的政党中此时(frac{V_i}{S_i+1})最大(相等时(i)最小)的政党。已知每个政党当前已经获得(a_i)张选票,问每个政党最多及最少可能获得多少个席位。

    前言

    今天晚上状态不行,这样一道题目浑浑噩噩地写了半个多小时。

    本来想写道数据结构题的,现在看来幸好没有写,否则按照现在的状态不知道要调到哪年。

    最多

    显然贪心+模拟即可,把剩余的所有票都给当前政党,然后模拟分席位的过程求出最大的席位数。

    最少

    考虑二分答案(t),然后再(DP)验证。

    由于选票数量要大于等于(5\%),因此实际上最多只有(20)个政党能得到选票。

    (f_{i,j})表示前(i)个政党获得(j)个席位需要的最少额外选票数,每次枚举(k)转移,要满足两个条件:

    • (frac{V_i}{k})大于(或大于等于)(frac{a_x}{t+1})
    • (V_ige5\% imes V)

    那么只要求出满足条件的最小的(V_i),再减去已有的(a_i),就是当前情况下所需最少选票数。

    具体实现详见代码。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100
    #define M 200
    #define INF 1e9
    using namespace std;
    int tot,p,n,m,a[N+5],s[N+5];
    I int BF_for_Mx(CI x)//贪心+模拟
    {
    	RI i,j,t;for(i=1;i<=n;++i) s[i]=0;for(a[x]+=p,i=1;i<=m;++s[t],++i)
    		for(t=0,j=1;j<=n;++j) 20*a[j]>=tot&&(!t||a[j]*(s[t]+1)>a[t]*(s[j]+1))&&(t=j);
    	return a[x]-=p,s[x];
    }
    int g[N+5],f[N+5][M+5];I bool cmp(CI x,CI y) {return a[x]>a[y];}
    I bool Check(CI x,CI t)//DP验证
    {
    	RI i,j,k;for(i=1;i<=min(n,20);++i) for(j=0;j<=m-t;++j)
    		if(f[i][j]=f[i-1][j],g[i]^x) for(k=1;k<=j;++k) f[i][j]=min(f[i][j],
    			f[i-1][j-k]+max(max((a[x]*k-(g[i]<x))/(t+1)+1,(tot-1)/20+1)-a[g[i]],0));//注意满足多重限制
    	return f[min(n,20)][m-t]<=p;
    }
    I int EF_for_Mn(CI x)//二分
    {
    	if(20*a[x]<tot) return 0;RI l=0,r=m,mid;
    	W(l<r) Check(x,mid=l+r-1>>1)?r=mid:l=mid+1;return r;//二分答案
    }
    int main()
    {
    	RI i;for(scanf("%d%d%d",&tot,&n,&m),p=tot,i=1;i<=n;++i) scanf("%d",a+i),p-=a[i];//p记录剩余选票数
    	for(i=1;i<=n;++i) printf("%d%c",BF_for_Mx(i)," 
    "[i==n]);
    	for(i=1;i<=n;++i) g[i]=i;sort(g+1,g+n+1,cmp);for(i=1;i<=m;++i) f[0][i]=INF;//初始化
    	for(i=1;i<=n;++i) printf("%d%c",EF_for_Mn(i)," 
    "[i==n]);return 0;
    }
    
  • 相关阅读:
    有关phpmailer的详细介绍及使用方法
    威盾解密
    PHP正则表达式验证是否含有中文
    php防止重复提交表单
    simplexml 对xml的增删改操作
    Javascript正则表达式完全学习手册
    利用JS做到隐藏div和显示div
    js控制div显示与隐藏
    后台登陆地址
    MVC 自定义路由
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ1181.html
Copyright © 2020-2023  润新知