题目链接: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;
}