题目链接:http://codeforces.com/contest/1020/problem/C
思路及代码来源:https://blog.csdn.net/black_horse2018/article/details/81672818
题目大意:
n个政客,m个政党(1≤n,m≤3000)
以下n行,第i行
一个整数pi(1≤pi≤m),代表第i个政客支持的政党;
一个整数ci(1≤ci≤1e9) ,代表买通第i个政客需要花的钱。
现在你代表1号党,问你怎么买通别的政客,
让己方赢得大选(比别党得票都高),且总花费最小,输出总花费。
题目思路:
从1票到n票,枚举1号党最终票数i,必有一个票数是最优解;
且由于i是固定的,则i对应的操作也是固定的,必须
①把超过我最终票数的“多数党”买至小于我的最终票数,挑便宜的买
②如果我买了“多数党”还没到最终票数,再去“少数党”里挑便宜的买,直至最终票数
③如果①的操作使我最终票数超过了当前枚举的最终票数,直接舍弃
比如1号党初状态0票,枚举过程中,设最终票数i=3票,
而此时有两个5票的,为了使他们小于3票必须至少各买3张,
这样自己就6票了,6>3直接将i=3舍弃。
代码:(枚举票数思维+排序贪心思想)
#include<stdio.h>
#include<queue>
#include<string.h>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<iostream>
#define e 0.000001
using namespace std;
typedef long long ll;
map<int,int>mapp;
struct fun{
int id; // 政党编号
ll s; // 收买选民的价钱
}f[3005];
bool cmp(fun x,fun y){
return x.s<y.s;
}
int per[3005];
int main(){
int n,m;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d %lld",&f[i].id,&f[i].s);
mapp[f[i].id]++;
}
sort(f+1,f+n+1,cmp);
ll ans=9999999999999;
for(int i=mapp[1];i<=n;i++){ // 枚举获胜的票数状态
memset(per,0,sizeof(per)); // per 数组保存的是 1 号政党相对于 i 政党要再收买 per[i] 的票才能获胜
int sum=0; // sum 保存的是 1 号政党相对于 所以 政党要再收买 sum 的票才能获胜
ll cnt=0; // 需要支付多少钱
for(int j=2;j<=m;j++){
if(mapp[j]>=i){
per[j]=mapp[j]-i+1;
sum+=per[j];
}
}
if(i-mapp[1]<sum) // 如果当前这种状态不能获胜,则丢弃
continue;
int k=i-mapp[1]-sum; // 剩余的票从其他比 1 号政党票少但价钱便宜的里边扣除
for(int i=1;i<=n;i++){
if(per[f[i].id]){
per[f[i].id]--;
cnt+=f[i].s;
}
else{
if(k!=0&&f[i].id!=1){
cnt+=f[i].s;
k--;
}
}
}
ans=min(ans,cnt);
}
printf("%lld\n",ans);
return 0;
}