题目描述:

C. Elections

time limit per test

2 seconds

memory limit per test

256 megabytes

input

standard input

output

standard output

As you know, majority of students and teachers of Summer Informatics School live in Berland for the most part of the year. Since corruption there is quite widespread, the following story is not uncommon.

Elections are coming. You know the number of voters and the number of parties — nn and mm respectively. For each voter you know the party he is going to vote for. However, he can easily change his vote given a certain amount of money. In particular, if you give ii-th voter cici bytecoins you can ask him to vote for any other party you choose.

The United Party of Berland has decided to perform a statistical study — you need to calculate the minimum number of bytecoins the Party needs to spend to ensure its victory. In order for a party to win the elections, it needs to receive strictly more votes than any other party.

Input

The first line of input contains two integers nn and mm (1≤n,m≤30001≤n,m≤3000) — the number of voters and the number of parties respectively.

Each of the following nn lines contains two integers pipi and cici (1≤pi≤m1≤pi≤m, 1≤ci≤1091≤ci≤109) — the index of this voter's preferred party and the number of bytecoins needed for him to reconsider his decision.

The United Party of Berland has the index 11.

Output

Print a single number — the minimum number of bytecoins needed for The United Party of Berland to win the elections.

Examples

input

Copy

1 2

1 100

output

Copy

0

input

Copy

5 5

2 100

3 200

4 300

5 400

5 900

output

Copy

500

input

Copy

5 5

2 100

3 200

4 300

5 800

5 900

output

Copy

600

Note

In the first sample, The United Party wins the elections even without buying extra votes.

In the second sample, The United Party can buy the votes of the first and the fourth voter. This way The Party gets two votes, while parties 33, 44 and 55 get one vote and party number 22 gets no votes.

In the third sample, The United Party can buy the votes of the first three voters and win, getting three votes against two votes of the fifth party.

思路:

刚开始:拿到题的时候思路非常混乱,一直想着贪心的做,想找出一种决策可以在每一步最优的情况下得到全局最优。可是试了几种决策后发现不是很恰当,还曾一度以为只要1党的票数高过总票数的一半就可以胜出。事实证明,是可以胜出,但不是最优。

看来直接贪心不太容易,怎么办呢?

首先想到我们不知道1党要得到多少票才能获胜,直接的想法是枚举这个票数k,最少1票最多n票,1党的票数要大于等于k,要使1党获胜,那其他党的票数就要小于k。算出每个k对应的最小花费的最小值,就是最终答案,时间复杂度为O(\(n^2\))。

实现方式有两种,第一种是以每个选民为着眼点,把选民按照收买价格从小到大排序(贪心的思想),用一个数组记录每个党的得票数,遍历选民只要改选民的党的票数\(\geq\)k,就收买这个选民,对应的这个党的票数就会减一,1党票数就会加一,并给选民做上标记,遍历完后如果1党票数\(\geq\)k,就算出这个k下的结果,如果还不够,就再遍历一遍,收买未收买的选民直到满足条件。

代码:

#include <iostream>
#include <algorithm>
#include <memory.h>
#include <climits>
#define max_n 3005
using namespace std;
int sum = 0;
long long total = 0;
long long minm = LLONG_MAX;
int m;
int n;
int cnt[max_n];
int check[max_n];
struct node
{
int f;
long long mon;
};
node a[max_n];
int cnta = 0;
int cmp(node a,node b)
{
return a.mon<b.mon;
}
int main()
{
cin >> n >> m;
for(int i = 0;i<n;i++)
{
int party;
long long money;
cin >> party >> money;
if(party==1)
{
sum += 1;
}
else
{
a[cnta].f = party;
a[cnta].mon = money;
cnta++;
cnt[party]++;
check[party] = 0;
}
}
sort(a,a+cnta,cmp);
for(int k = 1;k<=n;k++)
{
int s = sum;
total = 0;
memset(check,0,sizeof(check));
memset(cnt,0,sizeof(cnt));
for(int i = 0;i<cnta;i++)
{
cnt[a[i].f]++;
}
for(int i = 0;i<=cnta;i++)
{
if(cnt[a[i].f]>=k)
{
total += a[i].mon;
cnt[a[i].f]--;
check[i] = 1;
s++;
}
}
if(s>=k)
{
minm = min(minm,total);
}
else
{
for(int i = 0;s<k;i++)
{
if(check[i]==0)
{
total += a[i].mon;
s++;
}
}
if(minm>total)
{
minm = total;
}
}
}
cout << minm << endl;
return 0;
}

还有一种以党为着眼点,用一个结构体vector数组记录每个党的选民投票情况,当然也要排序,思路也是枚举,只不过实现稍复杂一点。

代码:

    #include <iostream>
#include <algorithm>
#include <memory.h>
#include <climits>
#define max_n 3005
using namespace std;
int sum = 0;
long long total = 0;
long long minm = LLONG_MAX;
int m;
int n;
int cnt = 0;
struct node
{
int id;
long long m;
};
node a[max_n];
int check[max_n];
struct voter
{
int id;
int mon;
};
vector<voter> pai[max_n];
int cmp(voter a,voter b)
{
return a.mon<b.mon;
}
int cmp2(node a,node b)
{
return a.m < b.m;
}
int main()
{
cin >> n >> m;
for(int i = 0;i<n;i++)
{
int party;
long long money;
cin >> party >> money;
if(party==1)
{
sum += 1;
}
else
{
voter v;
v.id = cnt;
v.mon = money;
a[cnt].id = cnt;
a[cnt].m = money;
cnt++;
pai[party].push_back(v);
}
}
for(int i = 2;i<=m;i++)
{
sort(pai[i].begin(),pai[i].end(),cmp);
}
sort(a,a+cnt,cmp2);
for(int k = 1;k<=n;k++)
{
int s = sum;
total = 0;
memset(check,0,sizeof(check));
for(int i = 2;i<=m;i++)
{
for(int j = 0;pai[i].size()-j>=k;j++)
{
total += pai[i][j].mon;
check[pai[i][j].id] = 1;
s++;
}
}
if(s>=k)
{
minm = min(minm,total);
}
else
{
for(int i = 0;s<k;i++)
{
if(check[a[i].id]==0)
{
total += a[i].m;
s++;
}
}
if(minm>total)
{
minm = total;
}
}
}
cout << minm << endl;
return 0;
}

实际上,这个k为自变量,收买价格为因变量是一个下凸函数,在1~n里有一个最小值,可以用三分的方法求极小值点。(关于三分的讲解见参考文章)

代码:

#include <iostream>
#include <algorithm>
#include <memory.h>
#include <climits>
#define max_n 3005
using namespace std;
int sum = 0;
long long total = 0;
long long minm = LLONG_MAX;
int m;
int n;
int cnt[max_n];
int check[max_n];
struct node
{
int f;
long long mon;
};
node a[max_n];
int cnta = 0;
int cmp(node a,node b)
{
return a.mon<b.mon;
}
long long cal(long long k)//相当于计算函数值,将版本一中的计算每种花费的情况独立成一个函数
{
int s = sum;
total = 0;
memset(check,0,sizeof(check));
memset(cnt,0,sizeof(cnt));
for(int i = 0; i<cnta; i++)
{
cnt[a[i].f]++;
}
for(int i = 0; i<=cnta; i++)
{
if(cnt[a[i].f]>=k)
{
total += a[i].mon;
cnt[a[i].f]--;
check[i] = 1;
s++;
}
}
if(s>=k)
{
//cout << "sum>=k" << endl;
return total;
}
else
{
//cout << "sum<k" << endl;
for(int i = 0; s<k; i++)
{
if(check[i]==0)
{
total += a[i].mon;
s++;
}
}
}
return total;
}
int main()
{
cin >> n >> m;
for(int i = 0;i<n;i++)
{
int party;
long long money;
cin >> party >> money;
if(party==1)
{
sum += 1;
}
else
{
a[cnta].f = party;
a[cnta].mon = money;
cnta++;
cnt[party]++;
check[party] = 0;
}
}
sort(a,a+cnta,cmp);
int l = 1;
int r = n;
while(r-l>3)//最后选出三个可能的极小值点
{
int mid = (l+r)>>1;
int mmid = (mid+r)>>1;
long long sum1 = cal(mid);
long long sum2 = cal(mmid);
if(sum1>sum2)//mmid更接近于极小值点
{
l = mid;
}
else//mid更接近于极小值点
{
r = mmid;
}
}
minm = cal(l);
for(int i = l+1;i<=r;i++)
{
long long ans = cal(i);
minm = min(minm,ans);
}
cout << minm << endl;
return 0;
}

参考文章:

键盘里的青春,三分算法概念,https://blog.csdn.net/qq_34374664/article/details/70141246

henuzxy,codeforces 1019 A. Elections (枚举或三分),https://blog.csdn.net/zhao5502169/article/details/81625471

闻道-问道,C. Elections(枚举+贪心),https://blog.csdn.net/a1046765624/article/details/81876925

Codeforces C. Elections(贪心枚举三分)的更多相关文章

  1. CodeForces - 1020C C - Elections(贪心+枚举)

    题目: 党派竞争投票 有n个人,m个党派,这n个人每个人有一个想要投的党派的编号Pi,如果想要这个人改变他的想法,那么就需要花费Ci元钱. 现在你是编号为1的党派,如果你想要赢(你的票数严格大于其他党 ...

  2. 【BZOJ3874】[AHOI&JSOI2014]宅男计划(贪心,三分)

    [BZOJ3874][AHOI&JSOI2014]宅男计划(贪心,三分) 题面 BZOJ 洛谷 题解 大力猜想一最长的天数和购买外卖的总次数是单峰的.感性理解一下就是买\(0\)次是\(0\) ...

  3. Codeforces 458C - Elections

    458C - Elections 思路: 三分凹形函数极小值域 代码: #include<bits/stdc++.h> using namespace std; #define ll lo ...

  4. codeforces 578c - weekness and poorness - 三分

    2017-08-27 17:24:07 writer:pprp 题意简述: • Codeforces 578C Weakness and poorness• 给定一个序列A• 一个区间的poornes ...

  5. CodeForces - 158B.Taxi (贪心)

    CodeForces - 158B.Taxi (贪心) 题意分析 首先对1234的个数分别统计,4人组的直接加上即可.然后让1和3成对处理,只有2种情况,第一种是1多,就让剩下的1和2组队处理,另外一 ...

  6. POJ 1018 Communication System 贪心+枚举

    看题传送门:http://poj.org/problem?id=1018 题目大意: 某公司要建立一套通信系统,该通信系统需要n种设备,而每种设备分别可以有m个厂家提供生产,而每个厂家生产的同种设备都 ...

  7. Codeforces Round #503 (by SIS, Div. 2) C. Elections(枚举,暴力)

    原文地址 C. Elections time limit per test 2 seconds memory limit per test 256 megabytes input standard i ...

  8. codeforces 613B B. Skills(枚举+二分+贪心)

    题目链接: B. Skills time limit per test 2 seconds memory limit per test 256 megabytes input standard inp ...

  9. 【codeforces 761C】Dasha and Password(贪心+枚举做法)

    time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...

随机推荐

  1. redis常用命令及持久化机制

    redis  常用命令 查找redis服务文件 find / -name  redis-server 查找配置文件 find / -name redis.conf 启动服务时候,要指定配置文件 启动r ...

  2. [转帖]Hyperledger Fabric 学习一:简介

    Hyperledger Fabric 学习一:简介 https://www.jianshu.com/p/f971858b70f3?utm_campaign=maleskine&utm_cont ...

  3. Django框架第九篇--Django和Ajax、序列化组件(serializers)、自定义分页器、模型表choice参数

    Django和Ajax 一.什么是Ajax AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步的Javascript和XML”.即使用Javascript语 ...

  4. MySQL表关系--外键

    一.外键前戏 如果我们把所有的信息都记录在一张表中会带来的问题: 1.表的结构不清晰 2.浪费磁盘空间 3.表的扩展性极差 所以我们要把这种表拆成几张不同的表,分析表与表之间的关系. 确定表与表之间的 ...

  5. Linux基础(03)gdb调试

    1. 安装GDB增强工具 (gef) * GDB的版本大于7.7 * wget -q -O- https://github.com/hugsy/gef/raw/master/scripts/gef.s ...

  6. 【LEETCODE】64、链表分类,medium&hard级别,题目:2,138,142,23

    package y2019.Algorithm.LinkedList.medium; import y2019.Algorithm.LinkedList.ListNode; /** * @Projec ...

  7. laravel 自定义验证 Validator::extend

    laravel 自定义验证 $messages = [ 'name.integer' => '名字不能为整型', 'name.max' => '长度不能超过5', ]; public st ...

  8. 【题解】Luogu P5300 [GXOI/GZOI2019]与或和

    原题传送门 我们珂以拆位,拆成一个个0/1矩阵 贡献珂以用全0,全1的子矩阵的个数来计算 全0,全1的子矩阵的个数珂以用悬线法/单调栈解决 #include <bits/stdc++.h> ...

  9. C#泛型集合之——队列与堆栈

    队列与堆栈基础 队列 1.操作: (1)创建及初始化: Queue<类型> 队列名 =new Queue<类型>()://空队列,无元素 Queue<类型> 队列名 ...

  10. sqlserver 2005 数据库的差异备份与还原

    找到一个可靠的步骤,点开链接:http://blog.csdn.net/kevindr/article/details/22154323