昨天CF的题,没想到自己会做的这么傻逼。。。真菜啊
题意:就N个选民,M个政党。已知每个选民原本打算投给的政党,已经收买他们的价格,问你花费最少的价格,使得1号政党的票数最多。
思路:直接考虑政党的得票数,使得1号政党高于这个票数,其他政党低于这个票数就行了。(1号政党可以远远高于这个得票数,但其他政党必须低于这个票数)然后从1到N枚举这个票数,维护最小值就行了。只要统计每个政党的得票数,那数据按收买价格从小到大排序就行了,对于高于这个票数的政党,就直接收买选民,知道不高于为止,如果到最后1号政党的票数还是低于枚举的票数,那么就从小到大再扫描一遍,只有是没被收买的都收买掉,直到达到为止。
这是O(N^2)的做法,不过我们可以考虑这样一件事,最小值一定出现在[1,N]这个区间,所如果画出花费金额和枚举的得票数就会发现这是一个凹函数,存在极小值。
所以我们可以三分求一下这个凹函数的极小值。
不理解三分的可以看下这个博客:https://blog.csdn.net/qq_34374664/article/details/70141246
这样我们就可以把时间复杂度降为O(NlogN)的了。
枚举代码如下:
#include<bits/stdc++.h>
using namespace std;
const int MAX = 3010;
typedef long long ll;
int N,M;
class Node{
public:
int p;
ll c;
};
Node q[MAX];
bool operator < (Node A,Node B){
return A.c < B.c;
}
int num[MAX],now[MAX];
bool book[MAX];
ll check(int Max){
for(int i=1;i<=M;++i) now[i] = num[i];
memset(book,false,sizeof(book));
ll sum = 0;
for(int i=1;i<=N;++i){
if(q[i].p == 1){
book[i] = true;
}
else if(now[q[i].p] < Max) continue;
else{
now[q[i].p]--;
sum += q[i].c;
now[1]++;
book[i] = true;
}
}
if(now[1] < Max){
for(int i=1;i<=N;++i){
if(now[1] == Max) break;
if(!book[i]){
sum += q[i].c;
now[1]++;
book[i] = true;
}
}
}
return sum;
}
int main(void){
cin >> N >> M;
for(int i=1;i<=N;++i){
cin >> q[i].p >> q[i].c;
num[q[i].p]++;
}
sort(q+1,q+1+N);
ll res = check(1);
for(int i=1;i<=N;++i){
res = min(check(i),res);
// cout << check(i) << endl;
}
cout << res << endl;
return 0;
}
三分做法如下:
#include<bits/stdc++.h>
using namespace std;
const int MAX = 3010;
typedef long long ll;
int N,M;
class Node{
public:
int p;
ll c;
};
Node q[MAX];
bool operator < (Node A,Node B){
return A.c < B.c;
}
int num[MAX],now[MAX];
bool book[MAX];
ll check(int Max){
for(int i=1;i<=M;++i) now[i] = num[i];
memset(book,false,sizeof(book));
ll sum = 0;
for(int i=1;i<=N;++i){
if(q[i].p == 1){
book[i] = true;
}
else if(now[q[i].p] < Max) continue;
else{
now[q[i].p]--;
sum += q[i].c;
now[1]++;
book[i] = true;
}
}
//如果一号政党还没达到要求的票数,就从小到大开始寻找,没被收买的一律收买
if(now[1] < Max){
for(int i=1;i<=N;++i){
if(now[1] == Max) break;
if(!book[i]){
sum += q[i].c;
now[1]++;
book[i] = true;
}
}
}
return sum;
}
int main(void){
cin >> N >> M;
for(int i=1;i<=N;++i){
cin >> q[i].p >> q[i].c;
num[q[i].p]++;
}
sort(q+1,q+1+N);
int l = 1,r = N;
while(r - l > 3){
int mid = (l+r)/2;
int rmid = (mid+r)/2;
ll sum1 = check(mid);
ll sum2 = check(rmid);
if(sum1 > sum2) l = mid;
else r = rmid;
}
ll res = check(l);
for(int i=l+1;i<=r;++i){
res = min(res,check(i));
}
cout << res << endl;
return 0;
}