codeforces #503 div 2 C Elections 【贪心+枚举】

题目链接:http://codeforces.com/contest/1020/problem/C

题意:

n个选民有对应的贿赂价格,最小要花费多少钱才能让 编号1的选票是最多的;

思路:

贪心应该是比较容易想到的,但是如何枚举才能求出正确答案;这里不是说把没选编号1的选民都按照花费从小到大排序,然后从小到大依次加起来就完事了,我们还有一些特殊情况需要考虑的;比如 如果我们从票数最高的那个人那里拿一票的话,我们真正需要得到的票数要比没拿要少一票,什么意思呢? 比如编号1有1张票,编号2有2票,如果我们不拿编号2的票,我们需要而外再拿多两张票数才能最大,如果我们拿一张编号2的话,编号1有两张,而编号2只有一张,不光如此,如果持票数最多的人,不止一个,要考虑的情况会更多;所以,我们需要去想一个比较容易解决所有情况的枚举方法去做这道题;如果你硬是要把所有的情况打成代码也是有可能AC的,但是不仅麻烦,而且容易出错,代码量也会很大;

想想,在什么情况下,编号1一定能够是票数最多的那个?找出所有编号票数最大的那个数maxn然后+1,1的票数增加到maxn+1,是不是就一定能是所有票数最多的那个,那么在排过序的花费中,从小到大依次累加到1的票数是maxn+1为止,就是一种花费的可能,这种情况是最坏的情况;有没有可能在编号1的票数为maxn的时候,是所有票数最多的那个呢?我们在不经过处理的时候,所有票数最多的数量是maxn,这个maxn可能是1的票数,也可能是别人的,是1的情况就不说了,如果别人的票数为maxn,我们是不是一定要优先拿掉这个票数为maxn的票,然后才考虑去拿别人的票,让自己的票数达到maxn?那maxn-1有没有可能?同样的,我们可以优先拿掉票数大于或者等于maxn-1的编号的票,使得这个编号的票数小于maxn-1,然后才考虑去拿别人的票,使得自己的票达到maxn-1,我们按照这样的思路去枚举每一种情况,然后把对应的ans的最小值作为结果,那么答案就出来了;

最初一直没发现用的INF是 int的,WA了不少次,因为数据比较大,要用long long

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>

using namespace std;

#define IOS ios::sync_with_stdio(false); cin.tie(0);

typedef long long ll;
const int Maxn = 3010;
const long long LINF = 1e18; 
const int INF = 0x3f3f3f3f;

struct point {
    ll p,cost;
} p[Maxn];

ll vis[Maxn],mn;

bool cmp (const point &a1, const point &a2) {
    return a1.cost < a2.cost;
}

int main (void)
{
    int n,m,x,y,M;
    cin >> n >> m;
    memset(vis,0,sizeof(vis));
    mn = 0; M = 0;
    for (int i = 1; i <= n; ++i) {
        cin >> x >> y;
        if(x != 1) {
            p[++M].p = x;
            p[M].cost = y;
        }
        vis[x]++;
        mn = max(mn,vis[x]); // 取出票数最大值
    }
    bool ok = true; //如果编号1已经是最大的那个就直接输出0
    for (int i = 2; i <= m; ++i) { if(vis[i] == mn) { ok = false; break; } }
    if(ok) { cout << "0" << endl; return 0; }
    sort(p+1,p+M+1,cmp);
    ll used[Maxn],tmp[Maxn],ans = INF,res;
    for (int i = mn; i > 0; --i) {
            res = 0;
            memset(used,0,sizeof(used));
            memcpy(tmp,vis,sizeof(tmp)); // 保证vis不会破坏,用tmp来代替vis
        for (int j = 1; j <= M; ++j) {
            if(tmp[p[j].p] > i) {
                tmp[1]++; tmp[p[j].p]--;
                res+=p[j].cost;
                used[j] = 1;
            }
        }
        for (int j = 1; tmp[1] <= i && j <= M; ++j) {
            if(!used[j]) { // 在优先拿掉票数大于i的编号的票数的时候,有些已经贿赂过了,
                    tmp[1]++;  //  一些已经贿赂过的就跳过
                    res+=p[j].cost;
            }
        }
        ans = min(res,ans);
    }
    cout << ans << endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/godleaf/article/details/81609111