題目鏈接: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;
}