About 贪心

P2587 [ZJOI2008] 泡泡堂

题目描述

第 XXXX 届 NOI 期间,为了加强各省选手之间的交流,组委会决定组织一场省际电子竞技大赛,每一个省的代表队由 n n n 名选手组成,比赛的项目是老少咸宜的网络游戏泡泡堂。每一场比赛前,对阵双方的教练向组委会提交一份参赛选手的名单,决定了选手上场的顺序,一经确定,不得修改。比赛中,双方的一号选手,二号选手……, n n n 号选手捉对厮杀,共进行 n n n 场比赛。每胜一场比赛得 2 2 2 分,平一场得 1 1 1 分,输一场不得分。最终将双方的单场得分相加得出总分,总分高的队伍晋级(总分相同抽签决定)。

作为浙江队的领队,你已经在事先将各省所有选手的泡泡堂水平了解的一清二楚,并将其用一个实力值来衡量。为简化问题,我们假定选手在游戏中完全不受任何外界因素干扰,即实力强的选手一定可以战胜实力弱的选手,而两个实力相同的选手一定会战平。由于完全不知道对手会使用何种策略来确定出场顺序,所以所有的队伍都采取了这样一种策略,就是完全随机决定出场顺序。

当然你不想这样不明不白的进行比赛。你想事先了解一下在最好与最坏的情况下,浙江队最终分别能得到多少分。

输入格式

输入文件的第一行为一个整数 n n n,表示每支代表队的人数。

接下来 n n n 行,每行一个整数,描述了 n n n 位浙江队的选手的实力值。

接下来 n n n 行,每行一个整数,描述了你的对手的 n n n 位选手的实力值。

输出格式

输入文件中包括两个用空格隔开的整数,分别表示浙江队在最好与最坏的情况下分别能得多少分。不要在行末输出多余的空白字符。

输入输出样例 #1

输入 #1

2
1
3
2
4

输出 #1

2 0

输入输出样例 #2

输入 #2

6
10000000
10000000
10000000
10000000
10000000
10000000
0
0
0
0
0
0

输出 #2

12 12

说明/提示

样例说明

1:我们分别称 4 4 4 位选手为 A , B , C , D A,B,C,D A,B,C,D 。则可能出现以下 4 4 4 种对战方式,最好情况下可得 2 2 2 分,最坏情况下得 0 0 0 分。

浙江???结果浙江???结果浙江???结果浙江???结果
一号ACADBCBD
二号BDBCADAC
得分0220

2:对手都是认真学习的好孩子,不会打游戏。无论如何排列出场顺序都无法改变被蹂躏的结果。浙江队总能取得全胜的结果。

20 % 20\% 20% 的数据中, 1 ≤ n ≤ 10 1\leq n\leq 10 1n10

40 % 40\% 40% 的数据中, 1 ≤ n ≤ 100 1\leq n\leq 100 1n100

60 % 60\% 60% 的数据中, 1 ≤ n ≤ 1000 1\leq n\leq 1000 1n1000

100 % 100\% 100% 的数据中, 1 ≤ n ≤ 100000 1\leq n\leq 100000 1n100000,且所有选手的实力值在 0 0 0 10000000 10000000 10000000 之间。

题解

首先想到肯定是田忌赛马的思路,用己方最弱的选手去对战对方最强的选手(送人头 )但是反例也很好举出,比如:

5
2 3 4 5 6 7
1 2 3 4 5 6

很明显,己方的选手完全可以完胜对方,不需要送人头了,那么这怎么办呢?

思路还是脱胎于田忌赛马,首先将两个数组排序,考虑下面的问题:

  • 什么时候可以派最弱的送人头?(是否可以不送人头,做到2分的贡献)
  • 我方最强的如何才能做到贡献最大?

对于第一个问题,如果己方最弱可以干过对方的最弱,那么肯定干啊。不然等着被对方干掉? 所以这就是第一种情况,能干就干,那么如果干不掉呢?最弱的贡献只能是碰掉对方最强的,完成使命。

对于第二个问题,我方的最强如果可以干过对方的最强,那么肯定干啊。不然等着对面最强干掉其他队友? 所以情况和上面很像,能干就干。

那么又出现了新的问题,解决这几个问题的顺序是什么呢?

  1. 如果己方最弱干得过对方最弱,那么干,贡献+=2
  2. 如果己方最强干得过对方最强,那么干,贡献+=2
  3. 如果以上的胜利条件都满足不了,那么派最弱的自杀,碰掉对面最强,注意,在这里要判断是否平手,说不定还有一点分呢!

这是对于己方的最好情况,那么最坏情况呢?
把双方换一下不就好了,首先显然,总分是 2 n 2n 2n ,那么己方的最低分就是 2 n − 对方的最高分 2n-对方的最高分 2n对方的最高分

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long 
int n;
int a[100010];
int b[100010];
int fun1(){
	int l=1,r=n;
	int x=1,y=n;
	int ans=0;
	while(l<=r && x<=y){
		if(a[l]>b[x]) ans+=2,l++,x++;
		else if(a[r]>b[y]) ans+=2,r--,y--;
		else ans+=(a[l]==b[y]),l++,y--;
	}
	return ans;
}
signed main(){
	scanf("%lld",&n);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	for(int i=1;i<=n;i++) scanf("%lld",&b[i]);
	sort(a+1,a+1+n);
	sort(b+1,b+1+n);
	printf("%lld ",fun1());
	swap(a,b);
	printf("%lld",2*n-fun1());
	return 0;
}

P6927 [ICPC 2016 WF] Swap Space

题目描述

你有许多电脑,它们的硬盘用不同的文件系统储存数据。你想要通过格式化来统一文件系统。格式化硬盘可能使它的容量发生变化。为了格式化,你需要买额外的硬盘。当然,你想要买容量最小的额外储存设备以便省钱。你可以按任意顺序格式化硬盘。格式化之前,你要把该硬盘上所有数据移到一个或更多的其他硬盘上(可以分割数据)。格式化后,该硬盘可以立刻开始使用。你没有必要把数据放到它原来所在的硬盘上。数据也可以被放到你额外买的硬盘上。举个例子,假设你有4个硬盘A、B、C、D,容量分别为6、1、3、3(GB)。新的文件系统下,它们的容量变为6、7、5、5(GB)。如果你只买1GB额外空间,你可以把B硬盘的数据放过去然后格式化硬盘B。现在你的B硬盘有7GB容量了,那么你就可以把A的数据放过去然后格式化A,最后把C、D的数据放到A上,再格式化C和D。

输入格式

第一行一个数 n ( 1 ≤ n ≤ 1 , 000 , 000 ) n(1≤n≤1,000,000) n(1n1,000,000),表示你的硬盘数。接下来 n n n 行,每行两个数 a a a b b b,分别表示该硬盘的原容量和新文件系统下的容量。所有容量都以 GB 为单位,且 1 ≤ a , b ≤ 1 , 000 , 000 , 000 1≤a,b≤1,000,000,000 1a,b1,000,000,000

输出格式

输出如果要格式化所有硬盘,你最少需要购买多少额外空间(GB)。

输入输出样例 #1

输入 #1

4
6 6
1 7
3 5
3 5

输出 #1

1

输入输出样例 #2

输入 #2

4
2 2
3 3
5 1
5 10

输出 #2

5

题解

显然需要把所有的硬盘排序,那么如何排序?

显然要按 a i , b i a_i,b_i ai,bi 的大小关系分类:

  1. a i < b i a_i < b_i ai<bi

假令有两对数 ( a 1 , b 1 ) , ( a 2 , b 2 ) (a_1,b_1),(a_2,b_2) (a1,b1),(a2,b2),且 a 1 ≤ a 2 a_1 \leq a_2 a1a2

  • 如果 b 1 ≥ a 2 b_1 \geq a_2 b1a2。则按照 12 的顺序,将带来 a 1 a_1 a1 的花费;而按照 21 的顺序,将带来 a 2 a_2 a2 的花费。所以按照 12 顺序更优。

  • 如果 b 1 < a 2 b_1 < a_2 b1<a2。则按照 12 的顺序,将带来 a 1 + a 2 − b 1 a_1 + a_2 - b_1 a1+a2b1 的花费;而按照 21 的顺序,将带来 a 2 a_2 a2 的花费。因为 a 1 − b 1 < 0 a_1 - b_1 < 0 a1b1<0,所以按照 12 顺序更优。

综上,此时按照 a i a_i ai 从小往大排序正确。


  1. a i > b i a_i > b_i ai>bi

假令有两对数 ( a 1 , b 1 ) , ( a 2 , b 2 ) (a_1,b_1),(a_2,b_2) (a1,b1),(a2,b2),且 b 1 ≥ b 2 b_1 \geq b_2 b1b2

  • 如果 b 1 ≥ a 2 b_1 \geq a_2 b1a2。则按照 12 的顺序,将带来 a 1 a_1 a1 的花费;而按照 21 的顺序,将带来 a 2 + a 1 − b 2 a_2 + a_1 - b_2 a2+a1b2 的花费。因为 b 2 − a 2 < 0 b_2 - a_2 < 0 b2a2<0,所以按照 12 顺序更优。

  • 如果 b 1 < a 2 b_1 < a_2 b1<a2。则按照 12 的顺序,将带来 a 1 + a 2 − b 1 a_1 + a_2 - b_1 a1+a2b1 的花费;而按照 21 的顺序,将带来 a 2 + a 1 − b 2 a_2 + a_1 - b_2 a2+a1b2。因为 b 2 − b 1 < 0 b_2 - b_1 < 0 b2b1<0,所以按照 12 顺序更优。

综上,此时按照 b i b_i bi 从大往小排序正确。
以上证明摘自这篇文章

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
struct node{
	int a1,b1;
}a[1000010],b[1000010];
int t;
int n,m;
bool cmp(node xx,node yy){
	return xx.a1<yy.a1;
}
bool cmp2(node xx,node yy){
	return xx.b1>yy.b1;
}
signed main(){
	while(~scanf("%lld",&t)){
		for(int i=1;i<=t;i++) a[i]={0,0},b[i]={0,0};
		n=0,m=0;
		for(int i=1;i<=t;i++){
			int x,y;
			scanf("%lld%lld",&x,&y);
			if(x<=y){
				a[++n].a1=x;
				a[n].b1=y;
			}else{
				b[++m].a1=x;
				b[m].b1=y;
			}
		}
		sort(a+1,a+n+1,cmp);
		sort(b+1,b+m+1,cmp2);
		int sum=0,ans=0;
		for(int i=1;i<=n;i++){
			ans=min(ans,sum-a[i].a1);
			sum+=(a[i].b1-a[i].a1);
		}
		for(int i=1;i<=m;i++){
			ans=min(ans,sum-b[i].a1);
			sum+=(b[i].b1-b[i].a1);
		}
		printf("%lld\n",abs(ans));
	}
	return 0;
}

P4053 [JSOI2007] 建筑抢修

题目描述

小刚在玩 JSOI 提供的一个称之为“建筑抢修”的电脑游戏:经过了一场激烈的战斗,T 部落消灭了所有 Z 部落的入侵者。但是 T 部落的基地里已经有 N N N 个建筑设施受到了严重的损伤,如果不尽快修复的话,这些建筑设施将会完全毁坏。现在的情况是:T 部落基地里只有一个修理工人,虽然他能瞬间到达任何一个建筑,但是修复每个建筑都需要一定的时间。同时,修理工人修理完一个建筑才能修理下一个建筑,不能同时修理多个建筑。如果某个建筑在一段时间之内没有完全修理完毕,这个建筑就报废了。你的任务是帮小刚合理的制订一个修理顺序,以抢修尽可能多的建筑。

输入格式

第一行,一个整数 N N N

接下来 N N N 行,每行两个整数 T 1 , T 2 T_1,T_2 T1,T2 描述一个建筑:修理这个建筑需要 T 1 T_1 T1 秒,如果在 T 2 T_2 T2 秒之内还没有修理完成,这个建筑就报废了。

输出格式

输出一个整数 S S S,表示最多可以抢修 S S S 个建筑。

输入输出样例 #1

输入 #1

4
100 200
200 1300
1000 1250
2000 3200

输出 #1

3

说明/提示

对于 100 % 100 \% 100% 的数据, 1 ≤ N < 150000 1 \le N < 150000 1N<150000 1 ≤ T 1 < T 2 < 2 31 1 \le T_1 < T_2 < 2^{31} 1T1<T2<231

题解

较简单的排序题,有人叫他反悔贪心
这道题目怎么反悔呢?
很明显按照修理建筑的T2排序,那么如果遇到不能修的呢?
反悔!
反悔也是要贪心的,反悔的一定是在这及之前T1最大的建筑,用一个优先队列维护就可以了

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n;
struct node{
	int x,y;
}a[150010];
bool cmp(node xx,node yy){
	if(xx.y==yy.y) return xx.x<yy.x;
	return xx.y<yy.y;
}
priority_queue<int>q;
signed main(){
	scanf("%lld",&n);
	for(int i=1;i<=n;i++) scanf("%lld%lld",&a[i].x,&a[i].y);
	sort(a+1,a+1+n,cmp);
	int k=0;
	int cnt=0;
	for(int i=1;i<=n;i++){
		k+=a[i].x;
		q.push(a[i].x);
		if(k<=a[i].y){
			cnt++;
		}else{
			k-=q.top();
			q.pop();
		}
	}
	printf("%lld",cnt);
	return 0;
}

P8113 [Cnoi2021] 自我主义的平衡者

题目背景

「流浪月球 ~ The Wandering Moon」在幻想乡上映了。

正如一千个人心中有一千个哈姆雷特,关于它的争议也在悄然蔓延。

不知何时起,一个叫做「花瓣」的平台出现,慢慢取代了市井之中的讨论声,成为了争议的主战场——因为它具有评分功能。平台上旁征博引、各抒己见的评分帖成为了幻想乡的居民们日常,一切看起来岁月静好。

直到平衡者的出现。

起初没有人在意在意这缕杂音,这只不过是一种无心的叛逆,一点无奈的情感,一次无聊的宣泄。直到平衡的思想深入人心,自我主义的狂潮达到顶峰,评分系统的秩序几近崩溃。

Cirno 觉得自己该做些什么了。

题目描述

Cirno 决定通过计算来说服与拯救被自我主义裹挟的众人。

参与评分的共有 n n n 位居民,平台限制的最高分为 m m m

每个居民在评分前都有一个心理预期分数 a i ( a i ∈ [ 0 , m ] ∩ Z ) a_i(a_i\in[0,m]\cap\mathbb{Z}) ai(ai[0,m]Z)

但人们并不会按照心理预期分数直接评分,而是当当前平台上的平均分严格高于自己的心理预期分数时,便评分道「分数太高了,打个 0 0 0 分平衡一下」,反之则说「分数太低了,打个满分( m m m分)平衡一下」。

初始时平台上的平均分为 0 0 0

为了证明这种评分方式对公平的破坏性,Cirno 希望你计算出这 n n n 位居民在不同的排列顺序下评分,平台上最终平均分可能的最大值与最小值。

输入格式

第一行,两个整数,用空格隔开,表示 n n n m m m

第二行, n n n 个整数,用空格隔开,表示 { a n } \{a_n\} {an}

输出格式

一行,两个实数,保留两位小数,分别表示平均分最大值和最小值。

输入输出样例 #1

输入 #1

5 5
1 2 3 4 5

输出 #1

4.00 2.00

输入输出样例 #2

输入 #2

7 114
23 75 35 17 101 55 73

输出 #2

81.43 32.57

说明/提示

数据范围与约定

对于 100 % 100\% 100% 的数据保证, 1 < n , m ≤ 1 0 5 1 < n,m\le 10^5 1<n,m105 a i ∈ [ 0 , m ] a_i \in [0,m] ai[0,m]

子任务

Subtask1(10 points): n ≤ 8 n \le 8 n8

Subtask2(10 points): n ≤ 20 n \le 20 n20

Subtask3(30 points): n ≤ 1 0 3 n \le 10^3 n103

Subtask4(50 points):无特殊限制。

题解

直觉题,直接按照大小排序,瞎搞一通就行

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long 
int n,m;
int a[100010];
signed main(){
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	sort(a+1,a+1+n);
	int sum=0;
	double k=0;
	for(int i=1;i<=n;i++){
		if(k<a[i]*1.0 || abs(a[i]*1.0-k)<(1e-7)) sum+=m;
		k=sum*1.0/i;
	}
	printf("%.2lf ",k);
	reverse(a+1,a+1+n);
	sum=0;
	k=0;
	for(int i=1;i<=n;i++){
		if(k<a[i]*1.0 || abs(a[i]*1.0-k)<(1e-7)) sum+=m;
		k=sum*1.0/i;
	}
	printf("%.2lf ",k);
	return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值