zoukankan      html  css  js  c++  java
  • 一道笔试题:贿赂选民

    问题描述

    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();
    }
    }
    
  • 相关阅读:
    hdu_5961_传递(bitset)
    hdu_5963_朋友(找规律)
    hdu_5968_异或密码(预处理+二分)
    hdu_5969_最大的位或(贪心)
    hdu_5965_扫雷(递推)
    hdu_5950_Recursive sequence(矩阵快速幂)
    hdu_5286_wyh2000 and sequence(分块)
    [BZOJ1951][SDOI2005]古代猪文(数论好题)
    [BZOJ2659][WC2012]算不出的算式(几何)
    [BZOJ 2656][ZJOI2012]数列(递归+高精度)
  • 原文地址:https://www.cnblogs.com/weiyinfu/p/9609810.html
Copyright © 2011-2022 走看看