一道笔试题:贿赂选民

问题描述

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 sons = new LinkedList<>();
}

long howmany(List 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 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 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 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();
}
}

转载于:https://www.cnblogs.com/weiyinfu/p/9609810.html

你可能感兴趣的:(java)