题目链接:http://codeforces.com/contest/1020/problem/C


题意描述:

有n个选民, m个政党  (n,m<=3000) ,第i个选民有两个数 pi ,ci (pi<=m, ci<=1e9),表示该选民投票给政党 pi ,但如果你给他 ci 金币,他会改变他的投票对象为政党 1 (政党编号1到m)。

现在要求使得政党 1 票数最高(无并列),所需花费的最少金币数。

题目分析:

这一题刚开始想要二分取恰好能获得票数最高时花费的金币作为答案,但是二分是错误的

比如说样例:

5 5      (5个选民,5个政党)

2 100  (1号选民投票2号政党,需要花费100金币才能使其投票给1号政党)

3 200

4 300

5 800

5 900

恰好能获得最高票数是2(买下4号和1号),花费=900

但是如果买了1号2号3号,也一样能获得最高票数,花费=600,显然这个才是正确答案。

正解:

通过枚举政党 1 获取(1到n)票数所需花费的金币,对能获得票数最高的情况取最小花费即可。

代码:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
#define ll long long
using namespace std;
int n,m,x,y;
vector<int>v[3005];
ll cal(int k){
vector<int>bk;
ll res = 0;
ll cnt = v[1].size();//政党1现有票数
for(int i = 2;i <= m;i++){
int num=v[i].size();
for(int j = 0; j < num;j++){
if(num-j >= k){ //num-j为政党i现有的票数,如果大于将要获得的票数k,需要将其买为政党1的票数
res += v[i][j];
cnt++;
}else bk.push_back(v[i][j]); //如果不大于就先丢桶里
}
}
if(cnt < k){ //如果政党1现有票数不够我们目标的k票
sort(bk.begin(),bk.end());//需要在丢桶里的选民中挑尽量便宜的买
for(int i = 0; i < bk.size() && cnt < k;i++){
res += bk[i];
cnt++;
}
}
return res; //枚举的k小于等于n,一定能达到票数k,不存在无解的情况
}
int main(){
while(~scanf("%d %d",&n,&m)){
for(int i = 1;i <= m;i++)v[i].clear();
for(int i = 1;i <= n;i++){
scanf("%d %d",&x,&y);
v[x].push_back(y);
}
for(int i = 1;i <= m;i++){//将各个政党的选民便宜的排前
sort(v[i].begin(),v[i].end());
}
ll ans=INF;
for(int k=1;k<=n;k++){ //枚举政党1的目标票数
ans=min(ans,cal(k)); //计算让政党1取得k票,其他政党都小于k票,所需的最小花费
}
printf("%lld\n",ans);
}
return 0;
}