版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_30358129/article/details/81624617
题目链接
题意:
告诉你有n个选民,m个党派,其中输入数据会告诉你每个人它选了哪个党派,并且他改变主意需要花多少钱。
你的任务是确保1号党派赢的前提下使用的钱最少,问最少花多少钱
1<=n,m<=3000
注意到党派和选民的数量比较少,而且不好直接求最少花费。
转化为验证答案合法性,但是枚举/二分花费的话也不好验证,改为枚举获胜时的选票数。(注意,不能二分选票,因为不满足单调性)
如果某党的选派大于等于该答案,那么一定需要花钱买该党的票,最后买完票后,如果1党的选票还是少于枚举的值,那么再继续买票。
线上做题时考虑的是开m个优先队列,分别购买m个党派的选票花费,然后就写炸了,一个小时都没搞出来。
看题解发现,可以遍历每个选民,如果该选民所选的党派的选票大于枚举的答案,那么购买该选民的票。这样考虑的话只需要把选民按照收买花费排序一下就行了。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 4000;
struct node{
int v,cost;
bool operator <(node b)const{
return cost<b.cost;
}
}p[N];
int main(){
// freopen("a.txt","r",stdin);
ios::sync_with_stdio(0);
int n,i,j,m,now = 0;
cin>>n>>m;
for(i = 1;i <= n;i ++){
cin>>p[i].v>>p[i].cost;
if(p[i].v==1)now ++;
}
sort(p+1,p+n+1);
ll minn = 9e18;
for(i = now;i <= n/2+1;i ++){
int num[N];
bool flag[N];
memset(num,0,sizeof(num));
memset(flag,0,sizeof(flag));
ll cost=0;
for(j = 1;j <= n;j ++){
num[p[j].v]++;
if(p[j].v==1) flag[j] = 1;
}
for(j = 1;j <= n;j ++){
if(num[p[j].v]>=i&&p[j].v!=1) {
cost += p[j].cost;
num[p[j].v]--;
num[1] ++;
flag[j] = 1;
}
}
for(j = 1;j <= n;j ++){
if(num[1]>=i)break;
if(!flag[j]){
cost += p[j].cost;
num[1] ++;
flag[j] = 1;
}
}
minn = min(minn,cost);
}
if(minn==9e18)minn = 0;
cout<<minn;
return 0;
}