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 n≤3时暴力讨论,恰好是所有人的渡河时间之和。n>3时,可以采取以下两种策略中最优的那种,把最慢的两个人送过河。
设最快的两个人的渡河时间为a,b,最慢的两个人的渡河时间为c,d,且 a ≤ b ≤ c ≤ d a \leq b \leq c \leq d a≤b≤c≤d
策略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;
}