CF round#503(div2) C. Elections (枚举+贪心)

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

Description:

有n个选民,m个候选人,假定现在知道每个选民当前会投的目标候选人pi, 并且可以花费ci去收买某个选民来改变它投票,要使得1号候选人选举胜出(票数严格大于其他候选人),最少要花费多少?

Input:

n,m,pi(1<=i<=n),ci(1<=i<=n)

Output:

最少花费

Analysis:

首先可以看到这里面有很多信息相互制约,候选人方面1号票数要大于其他人,选民方面最后花费要最少,候选人和选民之间又是票数相连接,感觉关系太复杂求解很困难,而且直接暴力复杂度也过大了。其实这时候可以考虑枚举一个什么东西来减少复杂性,那么要枚举什么呢?可以看到这个问题的问法很像是最小最大问题,要求最小的花费使得1号胜出。这样的话可以试着考虑二分,但是这里如果直接二分花费的话还是很复杂,换一个角度二分1号候选人的最小票数就更加自然了(等价于除1号外其他候选人的最大票数为x-1),这样判断1号候选人的最小票数为x是否可行时,将选民按照费用从小到大排序,然后从费用大的往小的走一遍,若第i个选民的目标候选人票数<x-1则不管,否则贿赂它并且做上已经贿赂的标记(vis[i]=1),在这个过程中同时记录下各候选人已经确定的票数,。然后就要开始判断1号候选人能不能达到x的票数了,这时候可以按费用从小到大访问选民一个一个贪心地贿赂直到1号候选人的票数达到x返回true,否则返回false. 这样就解决了判断问题,然而实际上这里是不能用二分的,因为事实上总费用和一号候选人的最小票数并不是完全正相关的。有可能一号候选人所需票数更多的时候总费用反而更少,因为在第一阶段从大到小扫选民的过程中可能有些费用高的,反而因为限制的下降而免去了。由此,不采用二分而是直接从1到n枚举一遍跟新答案即可。

#include<iostream>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<sstream>
#include<cmath>
#include<iterator>
#include<bitset>
#include<stdio.h>
#include<unordered_set>
#include<ctime>
#include<cstring>
#include<string>
#include<float.h>
using namespace std;
#define _for(i,a,b) for(int i=(a);i<(b);++i)
#define _rep(i,a,b) for(int i=(a);i<=(b);++i)
typedef long long LL;
const int INF = 1 << 30;
const int MOD = 1000000007;
const int maxn = 3005;

struct People {
	int p, c, id;
	bool operator <(const People & rhs)const {
		return c < rhs.c;
	}
};
People a[maxn];
int n,m;
int vis[maxn], cnt[maxn];

bool ok(int x,LL & sum) {
	sum = 0;
	memset(vis, 0, sizeof(vis));
	memset(cnt, 0, sizeof(cnt));
	for (int i = n; i >= 1; --i) {
		if (a[i].p == 1) { vis[i] = 1; cnt[1]++; continue; }
		if (cnt[a[i].p] + 1 < x) {
			cnt[a[i].p]++;
		}
		else {
			vis[i] = 1; cnt[1]++; sum += a[i].c;
		}
	}
	if (cnt[1] >= x)return true;
	for (int i = 1; i <= n&&cnt[1]<x; ++i) {
		if (!vis[i])cnt[1]++, sum += a[i].c;
	}
	return cnt[1] >= x;
}

int main()
{
	//freopen("C:\\Users\\admin\\Desktop\\in.txt", "r", stdin);
	//freopen("C:\\Users\\admin\\Desktop\\out.txt", "w", stdout);
	while (~scanf("%d%d", &n, &m)) {
		for (int i = 1; i <= n; ++i)scanf("%d%d", &a[i].p, &a[i].c);
		
		sort(a + 1, a + 1 + n);

		int L = 0, R = (n+1)/2;
		LL ans = 1e18;
		/*while (L <= R) {     错误的二分,WA on test 7
			int mid = L + (R - L) / 2;
			LL sum;
			if (ok(mid,sum)) {
				 L = mid + 1;
				 ans = min(ans, sum);
			}
			else R = mid - 1;
		}*/
		for (int i = 0; i <= n; ++i) {
			LL sum;
			if (ok(i, sum))ans = min(ans, sum);
		}
		printf("%lld\n", ans);
	}

	return 0;
}

猜你喜欢

转载自blog.csdn.net/tomandjake_/article/details/81748790
今日推荐