问题描述
n个选民给m个候选者投票,每个人只能投一票,最终票数超过全部其它人的人当选(并列第一不算当选)。每个人都喜欢吃糖,通过给某个人一定数量的糖,就可以让他投谁他就他谁。第一行输入两个整数选民数n和候选人数m,这两个数字都是[1,3000]区间内的整数。接下来n行每行两个数,第i行表示第i个人投谁、改变主意需要的糖果数。
问:最少花费多少个糖果才能使得1号候选人当选。
思路
贪心,优先贿赂哪些“胃口”小的选民。但是这样会有一个问题。
如:候选人1现有2票,候选人2现有3票,这时选民1要想超过候选人2有两种措施:贿赂投其它候选人的选民;贿赂候选人2的选民。这两种措施产生的效果是不一样的,选择措施1只会让候选人1跟候选人2平手,而措施2却能够一举翻盘。也就是说,此时,“邻之厚君之薄也”,敌人变弱等于我变强,候选人2的选民“一票顶2票”。就算候选人2的选民比较“贵”,但需要贿赂的人数少了,那也可能更加划算。
关键在于知道何时贿赂“便宜”的选民,何时贿赂“贵”的选民。
直接假设候选人1最终得票数为x,那么现在票数不小于x的候选人我要削弱它们,直到削弱到x-1为止。削弱“强候选人”之后,我的票数如果还是不够,我就可以去“便宜候选人”里面尽情“贪心”了。
那么如何假定最终票数呢?直接枚举最终票数的全部可能值。
最终票数的值必然不大于n/2+1。因为一山不容二虎。
最终票数的值必然大于最开始的票数,因为不可能越贿赂越少。
如果票数的值与贿赂糖果数之间满足凸性函数关系,那么三分法可以派上用场。但是不一定适用。
整体复杂度为O(n^2)
import java.util.*;
public class Main {
class Person {
int who;
long candy;
int id;
Person(int who, long candy, int id) {
this.who = who;
this.candy = candy;
this.id = id;
}
}
class Candidate {
LinkedList<Person> sons = new LinkedList<>();
}
long howmany(List<Candidate> candidates, Person[] people, int need, int one) {
int s = 0;
boolean used[] = new boolean[people.length];
for (int i = 0; i < candidates.size() && candidates.get(i).sons.size() >= need; i++) {
Iterator<Person> it = candidates.get(i).sons.iterator();
int buy = 0;
while (candidates.get(i).sons.size() - buy >= need) {
if (!it.hasNext()) return Long.MAX_VALUE;
Person p = it.next();
s += p.candy;
buy++;
one++;
used[p.id] = true;
}
}
//没够,需要凑
for (Person p : people) {
if (one >= need) break;
if (!used[p.id] && p.who != 1) {
one++;
s += p.candy;
}
}
return s;
}
Main() {
Scanner cin = new Scanner(System.in);
int n = cin.nextInt();
int m = cin.nextInt();
Person[] a = new Person[n];
for (int i = 0; i < n; i++) {
a[i] = new Person(cin.nextInt(), cin.nextInt(), i);
}
Arrays.sort(a, Comparator.comparing(x -> x.candy));
List<Candidate> piao = new ArrayList<>(m + 1);
for (int i = 0; i <= m; i++) {
piao.add(new Candidate());
}
for (Person p : a) {
piao.get(p.who).sons.add(p);
}
int one = piao.get(1).sons.size();
List<Candidate> other = piao.subList(1, piao.size());
other.sort(Comparator.comparing(x -> -x.sons.size()));
long s = Long.MAX_VALUE;
for (int i = one; i <= n / 2 + 1; i++) {
long use = howmany(other, a, i, one);
s = Math.min(s, use);
}
System.out.println(s);
}
public static void main(String[] args) {
new Main();
}
}