1. 程式人生 > >codeforces #503 div 2 C Elections 【貪心+列舉】

codeforces #503 div 2 C Elections 【貪心+列舉】

題意:

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;
}