• 一道笔试题:贿赂选民


    问题描述

    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();
    }
    }
    
  • 相关阅读:
    asp.net 下载文件
    Asp.Net中用iframe解决模态窗口文件下载问题(转)
    如何获取网站的根目录(js或者asp.net)
    java中日期加减计算(转)
    网页颜色选择器
    信仰基督教的好处
    基因芯片数据字段
    独立分量分析(ICA)
    GenePix® Pro 文件格式
    OBO文件中的标签的含义/意思/意义
  • 原文地址:https://www.cnblogs.com/weiyinfu/p/9609810.html
Copyright © 2020-2023  润新知