2018SCUACM FallTrainingB 1 贪心

6 篇文章 0 订阅
3 篇文章 0 订阅

emmmmm第一次出题,好像难度还是没把握的太好。。。emmmm不过。。恭喜AK
题目链接2018SCUACM FallTrainingB 1 贪心


A - 王泥煤的泥煤

Description

As we all know,有一种非常珍贵的煤球,叫做泥煤(peat)。众所周知,泥煤有很多不同的形状、大小、重量,因此不同种类的泥煤具有不同的价值。现在王泥煤同学垄断了泥煤市场,所以他可以安排每种泥煤的价格从而获得最大利润(成本为0)。

王泥煤有26种泥煤,分别用26个英文字母来表示。王泥煤不在乎字母的大小写,即a和A指的都是同一种泥煤。对于每种泥煤,它的价格一定是1-26中的一个整数,且任意两个泥煤的价格都不同。

王泥煤的数学很不好,但是他想知道他能赚多少钱,所以他来找你帮帮忙。输入王泥煤已经有的泥煤,请你告诉王泥煤他可以获得的最大利润是多少?

Input

输入一个字符串s,保证strlen(s) <= 10000,保证s完全由字母组成。

Output

输出包括一行,为每种泥煤分配一个价格,你不需要输出这个价格,只需要告诉王泥煤他能获得的最大利润。

Sample Input

dad

Sample Output

77


emmmm签到题,出现越多的字符权值分配越高,证明参见排序不等式,就不写太多了,直接上代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 5;
char s[maxn];
int cnt[26];
int main() {
	while(~scanf("%s", s)) {
		memset(cnt, 0, sizeof(cnt));
		for(int i = 0; s[i]; i++) cnt[tolower(s[i]) - 'a']++;
		sort(cnt, cnt+26);
		int ans = 0;
		for(int i = 0; i < 26; i++) ans += (i+1)*cnt[i];
		printf("%d\n", ans);
	}
	return 0;
}

B - 王泥煤的相亲大会

Description

It is well known that,王泥煤参加了一场相亲大会,想要找到自己的另一半。但是到了会场他发现好像有什么不对。他发现这场相亲大会不在乎双方的性别(也就是说任意两个人都能组成一队cp),而在乎的是两个人的love值。

相亲大会共有n个人参加,每个人都有一些泥煤,并且会把这些泥煤当做定情信物。当两个人组成cp时,他们的love值为两个人的泥煤数量之和。本来任何两个人都能组成cp,可惜月有阴晴圆缺,当两个人的love值超过m时,他们就会发生m年之痒,从而无法组成cp,如果一个人无法和别人组成cp,那么他将自动成为一条单身狗。每个人至多和一个人组成cp(劈腿禁止)。

现在王泥煤知道n和m,以及所有人的泥煤数量。他想问问你,最后组成的cp组数和单身狗的数量之和最少为多少呢?

Input

第一行包括两个正整数n和m(n <= 10000),接下来n行,第i行包括一个正整数,表示第i个人的泥煤数量,且保证每个人的泥煤数量不超过m

Output

输出包括一行,请告诉王泥煤cp的数量和单身狗的数量之和的最小值

Sample Input

3 6

1

2

3

Sample Output

2


题意:n个人分组,每组1-2人,每组内的泥煤数量之和不能大于m,问最少要分成多少组

考虑权值最大的人,如果他不能和权值最小的人分为一组,那么显然他不能和任何人分为一组,只能单独分组。

如果它能和权值最小的人分为一组,那么考虑权值最小的人,要充分发挥权值最小的人的作用,就要让和他分成一组的那个人的权值尽量大,所以此时将最大和最小分为一组是最优。

也比较简单,代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 5;
int a[maxn];
int main() {
	int n, m;
	while(~scanf("%d%d", &n, &m)) {
		for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
		sort(a+1, a+n+1);
		int i = 1, j = n;
		int cnt = 0;
		while(i <= j) {
			if(a[i] + a[j] <= m) i++;
			j--; cnt++;
		}
		printf("%d\n", cnt);
	}
	return 0;
}

C - 合并泥煤I

Description

It is universally accknowledged that 泥煤(peat)是一个非常珍贵的收藏品,越大的泥煤收藏价值越高。

一天,王泥煤找到了阿拉伯神灯,也就是阿拉丁神灯的弟弟,他向阿拉伯神灯许了一个愿望,从此获得了一个超能力,可以将两个泥煤合并为更大的泥煤。但是这个能力非常的鸡肋,王泥煤需要支付与这两块泥煤等价值的财富才能将他们合并。

比如:王泥煤把两块价值分别为3和5的泥煤合并,可以得到一块价值为8的泥煤,但是要消耗3+5的财富。

王泥煤想知道,他将手中的n块泥煤合并到只剩下一块之后,最少需要花费多少财富。

Input

第一行为一个整数n(n <= 20000),代表王泥煤拥有的泥煤数量,接下来n行,每行一个整数a_i(1 <= a_i <= 50000),代表每个泥煤的价值

Output

输出包括一行,请告诉王泥煤他需要花费的最少财富。

Sample Input

3

8

5

8

Sample Output

34


emmmm熟悉的人知道这就是Huffman树求权值,贪心策略是每次选取权值最小的两个数合并,至于证明emmmm可以自己百度Huffman。

这题2e4个不超过5e4的数,极限情况显然会爆int,所以要开long long。

2e4的数据范围对 O ( n 2 ) O(n^2) O(n2)的数据来说有一点危险,所以要把取最小值的部分用优先队列来实现,复杂度O(nlogn)

代码:

#include<cstdio>
#include<iostream>
#include<queue>
using namespace std;
struct Node {
	int val;
	Node(int val) : val(val) {}
	bool operator < (const Node& a) const {
		return val > a.val;
	}
};
priority_queue<Node> q;
int main() {
	int n;
	while(~scanf("%d", &n)) {
		while(!q.empty()) q.pop();
		for(int i = 1; i <= n; i++) {
			int t; scanf("%d", &t);
			q.push((Node)t);
		}
		long long ans = 0;
		while(q.size() > 1) {
			int a = q.top().val; q.pop();
			int b = q.top().val; q.pop();
			ans += a+b; q.push((Node)(a+b));
		}
		printf("%lld\n", ans);
	}
	return 0;
}

D - 泥煤过河

Description

As known to all,泥煤精灵们为了逃避王泥煤的追捕,跨越了千山万水,来到了一条河。河里只有一条船,船上最多只能坐两个泥煤精灵。泥煤精灵们必须渡过这条河,所以每次有精灵把船开过去,都必须有一个精灵负责把船开回来接其他的泥煤精灵。

每个泥煤精灵有一个渡河的时间t,当两个精灵同乘一船时,他们渡河的时间是两个泥煤精灵中渡河时间最多的那个(也就是说渡河时间为2和4的两个精灵同乘一船时,渡河时间为4)

现在有n个泥煤精灵,请你帮他们计算一下泥煤精灵们过河所需要的最短时间

Input

输入包含多组数据,第一行一个整数T(1 <= T <= 20),表示数据的组数

每组数据的第一行有一个整数n(1 <= N <= 1000),接下来一行有n个整数,表示每个泥煤精灵渡河的时间t_i(1 <= t_i <= 100)

Output

输出包括一行,求n个小精灵渡河所需要的最短时间

Sample Input

1

4

1 2 5 10

Sample Output

17


贪心策略是: n ≤ 3 n \leq 3 n3时暴力讨论,恰好是所有人的渡河时间之和。n>3时,可以采取以下两种策略中最优的那种,把最慢的两个人送过河。

设最快的两个人的渡河时间为a,b,最慢的两个人的渡河时间为c,d,且 a ≤ b ≤ c ≤ d a \leq b \leq c \leq d abcd

策略1:ab过河,a回来,cd过河,b回来(耗时a+2b+d)

策略2:ac过河,a回来,ad过河,a回来(耗时2a+c+d)

至于这个结论的证明比较复杂,也不做详细讨论。(其实真正打比赛哪有那么多时间去证明结论。。。打个表能找到规律就直接交了。。。证明都是事后。。。)

知道结论这题就很简单了。。。代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 1005;
int a[maxn];
int main() {
	int T; scanf("%d", &T);
	while(T--) {
		int n; scanf("%d", &n);
		for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
		sort(a+1, a+n+1);
		int ans = 0;
		while(n >= 4) {
			ans += min(a[1] + (a[2]<<1) + a[n], (a[1]<<1) + a[n-1] + a[n]);
			n -= 2;
		}
		switch(n) {
			case 3: ans += a[3] + a[2] + a[1]; break;
			case 2: ans += a[2]; break;
			case 1: ans += a[1]; break;
		}
		printf("%d\n", ans);
	}
	return 0;
}

E - 合并泥煤II

Description

It’s commonly known that 泥煤(peat)是一个非常珍贵的收藏品,越大的泥煤收藏价值越高。

但是阿拉伯神灯非常的不靠谱,有一天王泥煤发现他的超能力出现了一些八阿哥(bug),经过0x7fffffff次试验,他终于发现了规律。

现在,当王泥煤将两个价值为v_1和v_2的泥煤合并时,他们的价值竟然会变成2×sqrt{v_1 × v_2}

王泥煤想知道,当他将n块泥煤不断合并到剩下一块的时候,最后一块的最小质量是多少。

Input

第一行输入一个整数n(1 <= n <= 100),表示泥煤的数量,接下来n行输入每个泥煤的价值v_i,(1 <= v_i <= 10000)

Output

输出包括一行,给出最后一块泥煤的最小质量,保留三位小数

Sample Input

3

72

30

50

Sample Output

120.000


这题类似前面的Huffman,只不过不是算权值,而是算合并后的最小值。显然越大的数经过越多次开方后减小的越多,所以做过上面那题的话很自然就会想到这题的解法:

每次把两个最大的数合并。

这题有一些玄学bug(比如我用%.3lf不过,改成%.3f就AC了)

代码:

#include<cstdio>
#include<iostream>
#include<queue>
#include<cmath>
using namespace std;
priority_queue<double> q;
int main() {
	int n;
	while(~scanf("%d", &n)) {
		for(int i = 1; i <= n; i++) {
			double t; scanf("%lf", &t);
			q.push(t);
		}
		while(!q.empty()) {
			double a = q.top(); q.pop();
			if(q.empty()) {
				printf("%.3f\n", a);
				break;
			}
			double b = q.top(); q.pop();
			q.push(2*sqrt(a*b));
		}
	}
	return 0;
}

上面这份代码是延续的C题,所以仍然采用了priority_queue,但是我们发现,合并两个最大值后得到的值,在原数组中一定还是最大的。所以这题我们只需要进行一遍排序即可,也是能过的。代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 105;
double a[maxn];
int main() {
	int n;
	while(~scanf("%d", &n)) {
		for(int i = 1; i <= n; i++) scanf("%lf", &a[i]);
		sort(a+1, a+n+1);
		while(n > 1) {
			a[n-1] = 2*sqrt(a[n-1]*a[n]);
			n--;
		}
		printf("%.3f\n", a[1]);
	}
	return 0;
}

F - 泥煤精灵的工作

Description

Everybody knows,泥煤精灵们为了逃避王泥煤的追捕,跨越了千山万水,来到了匹卅大学,他们决定帮助匹卅大学的ACM队员做一些防AK题。但是防AK题太难了,就算是把王泥煤叫来也不会做(所以如果待会儿你做出来了防AK题,那就是泥煤精灵在默默的帮助你!)。泥煤精灵们一直相信一句话,勤能补拙,所以他们希望一天当中的任何一个时刻都有精灵在刷题?

泥煤精灵们把一天划分为T段(用1,2,3,…,T来表示),而每个精灵可以负责一段时间的刷题(也可以偷懒),n个小精灵不必全部参与刷题

泥煤精灵们想知道,最少要安排多少个小精灵参与刷题,才能让一天当中任何一个时刻都有精灵在刷题呢?

Input

第一行两个整数N(1 <= N <= 25000)和T(1 <= T <= 1000000),N表示泥煤精灵的个数

接下来N行,每行两个整数L,R,表示这个小精灵可以在[L, R]时间段内进行刷题

Output

每组数据一行,输出最少需要的泥煤精灵的数量。如果不可能一直都在刷题,输出-1

Sample Input

3 10

1 7

3 6

6 10

Sample Output

2


经典的区间覆盖问题。从n个区间中选出一部分,覆盖1-T的每一段。求最少要用多少个区间。

从左到右覆盖区间,用一个变量now记录当前还没有覆盖的区间的首位置,now的初始值为1。每次选取起点<=now,且终点最大的那个区间。覆盖后更新now。如果不存在这样的区间,那么说明不能完全覆盖。

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef pair<int, int> Node;
const int maxn = 25005;
Node a[maxn];
int main() {
	int n, m;
	while(~scanf("%d%d", &n, &m)) {
		for(int i = 1; i <= n; i++)
			scanf("%d%d", &a[i].first, &a[i].second);
		sort(a+1, a+n+1);
		int now = 1, cnt = 0;
		int idx = 1;
		while(idx <= n) {
			int tmp = -1;
			while(idx <= n && a[idx].first <= now) {
				tmp = max(tmp, a[idx].second);
				idx++;
			}
			if(tmp < now) break;
			now = tmp+1; cnt++;
		}
		printf(now <= m ? "-1\n" : "%d\n", cnt);
	}
	return 0;
}

G - 田鸡赛泥煤

Description

It is common knowledge that 从前有一个很聪明的田鸡,他和一个田鸭玩赛泥煤,比赛的规则是这样的:两个人同时拿出一块泥煤,如果两块泥煤同样大,则不计分;否则,泥煤较大的一方获得胜利,获胜方可以获得200分,同时失败方要扣掉200分。田鸭的泥煤分为大,中,小三种,而田鸡的泥煤只有大,中,小三种。显而易见,田鸭的字体比田鸡的粗,所以田鸭的泥煤也比田鸡的大。虽然看起来田鸭的泥煤都比田鸡的大,但是田鸡用小的泥煤去比田鸭的大泥煤,用大的泥煤去比田鸭的中泥煤,用中的泥煤去比田鸭的小泥煤,最后获得了胜利。

今天,田鸡要和王泥煤比赛泥煤,今天他们各有n块泥煤,假设田鸡今天是天选之人,你能帮忙算算田鸡最多能获得多少分吗??

Input

输入包括多个测试数据,每组测试数据第一行包括一个整数n(1 <= n <= 1000),第二行包括n个整数,表示田鸡的n块泥煤的大小,第三行包括n个整数,表示王泥煤的n块泥煤的大小。

输入将以仅含有数字0的一行代表结束

Output

对于每组测试数据输出一行,给出田鸡最多能获得的分数

Sample Input

3

92 83 71

95 87 74

2

20 20

20 20

2

20 19

22 18

0

Sample Output

200

0

0


贪心策略:称双方最快的马为快马,最慢的马为慢马

首先比较双方的快马,若我的快马比你快,则我选择两匹快马比赛

若我的快马比你慢,则我选择我的慢马与你的快马比赛

若我的快马与你的快马相同,则比较双方的慢马

若我的慢马比你的慢马快,则我选择双方慢马比赛

若我的慢马比你的慢马慢或相等,则选择我的慢马与你的快马比赛

注意,我的慢马和你的快马比赛时不一定是输,还有可能平

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1005;
int a[maxn], b[maxn];
int main() {
	int n;
	while(~scanf("%d", &n) && n) {
		for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
		for(int i = 1; i <= n; i++) scanf("%d", &b[i]);
		sort(a+1, a+n+1); sort(b+1, b+n+1);
		int al = 1, ar = n, bl = 1, br = n;
		int score = 0;
		for(int i = 1; i <= n; i++) {
			if(a[ar] > b[br]) {
				ar--; br--; score += 200;
			}
			else if(a[ar] < b[br]) {
				al++; br--; score -= 200;
			}
			else {
				if(a[al] > b[bl]) {
					al++; bl++; score += 200;
				}
				else {
					if(a[al] < b[br]) score -= 200;
					al++; br--;
				}
			}
		}
		printf("%d\n", score);
	}
	return 0;
}

H - 最美的泥煤

Description

It is particularly notorious that 泥煤(peat)是有一些美丽值的,但是往往要很多泥煤放在一起才能最好的体现出他们的美丽值。这些泥煤不能随意的放在一起,而是要遵循一些规则:

【1】这些泥煤的美丽值总和不能超过x

【2】这些泥煤的美丽值的中位数不能低于y

【3】任何一块泥煤的美丽值不能超过p

现在王泥煤有一个含有n个储物格的泥煤收藏柜,而且他已经有k块泥煤,所以他可能还需要购买一些泥煤。王泥煤想要让他收藏的泥煤满足上面的条件,请你帮他挑选一些他还需要购买的泥煤。

Input

第一行包括5个整数,n,k,p,x,y,含义如题

第二行包括k个整数,表示王泥煤已经拥有的k块泥煤的美丽值

1 <= n <= 999,n%2=1,0 <= k < n,1 <= p <= 1000,n <= x <= n × p,1 <= y <= p

每块泥煤的美丽值a_i满足1 <= a_i <= p

Output

如果王泥煤无法收集一个完美的收藏柜,输出-1

否则输出n-k个整数,表示王泥煤需要购买的泥煤的美丽值,如果有多解,输出任意一个

Examples

Input

5 3 5 18 4

3 5 4

Output

4 1

Input

5 3 5 16 4

5 5 5

Output

-1


贪心策略很简单:只选择添1或y

当我当前的中位数比y大的时候,添1;当我当前的中位数比y小的时候,添y

这样的选择能尽可能使中位数偏向于y,且使总和尽量小。如果采取这个策略仍然不能满足条件,那就不存在满足条件的解。

其实也就是添加y直到>=y的数的个数超过一半,然后剩余的全部填1。

思路很简单,但是实现起来还是有很多细节的。我没太去纠结细节问题,所以代码有点乱,将就看吧emmmmm代码:

#include<bits/stdc++.h>
using namespace std;
int main() {
	int n, k, p, x, y;
	scanf("%d%d%d%d%d", &n, &k, &p ,&x, &y);
	int cnt = 0, sum = 0;
	for(int i = 1; i <= k; i++) {
		int t; scanf("%d", &t);
		sum += t;
		if(t >= y) cnt++;
	}
	if(cnt <= n>>1) {
		int d = ((n+1)>>1) - cnt;
		if(d > n - k) printf("-1\n");
		else {
			k += d; sum += y*d;
			
			sum += n-k;
			if(sum > x) printf("-1\n");
			else {
				while(d--) printf("%d ", y);
				for(; k < n; k++) printf("1 ");
				printf("\n");
			}
		}
	}
	else {
		sum += n-k;
		if(sum > x) printf("-1\n");
		else {
			for(; k < n; k++) printf("1 ");
			printf("\n");
		}
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值