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