Codeforces Round #378 (Div. 2) 前四题题解

A.Grasshopper And the String

题目链接:

http://codeforces.com/contest/733/problem/A

题目大意:

有一只蚂蚱,要跳过一串字符串,但是它只能踩在元音字母'A', 'E', 'I', 'O', 'U' 然后乱入一个’Y‘这六个字母上,然后要你求出这只蚂蚱要跳过这个字符串每一跳的最大步数(字母数)是多少。

题解:

水题。按照题目的要求来遍历一遍字符串然后到了那六个字母就记录下走过了多少个字母,然后更新一下最大步数,输出就好了。总结起来就是求字符串元音字母的最大相隔距离。

代码:
#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;

#define ll long long
#define INF 0x3f3f3f3f
#define LL_INF 0x3f3f3f3f3f3f3f3f
#define MAX 1010

char str[MAX];

bool isIn(char c)
{
	if (c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U' || c == 'Y' || c == '\0')
		return true;
	return false;
}

int main()
{
	//freopen("debug\\in.txt", "r", stdin);
	//freopen("CON", "w", stdout);
	int i, j, k;
	scanf("%s", str);
	int step = 0, tmp = 0; //step是最终的步数,tmp是两个元音字母间的临时距离
	//printf("%s\n", str);
	int len = strlen(str);
	for (int i = 0; i <= len; ++i) {
		if (!isIn(str[i]) )
			tmp++;
		else {
			step = max(step, ++tmp);
			tmp = 0;
		}
	}
	printf("%d\n", step);
	return 0;
}

B.Parade

题目链接:

http://codeforces.com/contest/733/problem/B

题目大意:

某国要举行一次阅兵,现在正在为阅兵的首次抬左脚还是右脚而烦恼中。阅兵的观赏性可以用一个value值度量,value值越高表示观赏性越高。该国的观赏性值是由抬左脚的士兵数量L剪掉抬右脚的士兵数量R的绝对值得来,即value = | L - R |。现在给出每一列士兵抬左脚跟抬右脚的人数,要你选择其中的一列,交换这一列当中抬左脚跟抬右脚的人数,使得最终的观赏性value值得到尽可能地提高。没有输出0。

题解:

水题。直接照着题目的思路来就好了,分析都不用分析,遍历每一列,尝试交换,查看结果,有提高就记录没提高就抬走下一个。

代码:

#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;

#define ll long long
#define INF 0x3f3f3f3f
#define LL_INF 0x3f3f3f3f3f3f3f3f
#define MAX 100100

int l[MAX], r[MAX];

int main()
{
	//freopen("debug\\in.txt", "r", stdin);
	//freopen("CON", "w", stdout);
	int i, j, k;
	int n;
	scanf("%d", &n);
	int left = 0, right = 0;
	for (int i = 1; i <= n; ++i) {
		scanf("%d %d", &l[i], &r[i]);
		left += l[i];
		right += r[i];
	}
	int value = abs(left - right), index = 0;
	for (int i = 1; i <= n; ++i) {
		int v = abs((left - l[i] + r[i]) - (right - r[i] + l[i]));
		if (v > value) { //松弛操作
			value = v;
			index = i;
		}
	}
	printf("%d\n", index);
	return 0;
}


C.Epidemic in Monstropolis

题目链接:

http://codeforces.com/contest/733/problem/C

题目大意:

给你一排初始怪兽,每只怪兽给出重量。怪兽可以吃掉它相邻的重量比它低怪兽。并且怪兽们的重量遵循重量守恒定律,即吃掉了多少重量的怪兽自身就增加多少重量,吃多少长多少。现在题目给了一排目标怪兽的重量,问你初始怪兽们能不能吃呀吃,吃成目标怪兽们的重量。并且给出吃的过程。

题解:

题目有几个关键信息:

1、相邻。每只怪兽只能吃相邻的怪兽。

2、重量轻。怪兽只能吃重量比自己轻的。

3、质量守恒。吃多少长多少。

那么一步一步来。首先分析一下哪些情况不可能吃出来:

1、前后质量不守恒的。如:

初始: 2 3 3 3 3

目标: 6 6 6

前面总重量是14,后面是18,怎么可能吃成后面。

2、连续区间不可得的。因为是只能吃相邻的,所以每一个目标重量都有一段连续的初始重量相加得来。如:

初始: 2 4 4 4 4

目标: 6 6 6

目标的第一个6,可以由初始的2 4相加得来。但是第二个6就没办法由4 4得来了,所以不行。

3、连续区间内谁也吃不了谁的。如:

初始: 2 4 4 4 4

目标: 6 8 4

乍一看好像可以,但是目标的8由初始的4 4相加得来,那么这两个4谁吃谁呢?谁也吃不了谁,所以这种情况不行。

好,讨论完不可能的情况,就得来看看可以的情况,我们要怎么办了。

上述三种情况都符合的话,是不是可以求出每个目标怪兽对应的初始怪兽区间了?明显可以。

那么已经知道了目标怪兽都由哪些初始怪兽得来,剩下的是不是只需要考虑怎么个吃法了?是的。

那么是不是找到初始怪兽区间内重量最大的那一只,从那一只开始,挑一个方向吃,比如先往左边吃吃吃,吃完往右边吃吃吃,是不是吃的顺序很容易就出来了?

这就可以了。注意的是往左边吃,当前怪兽的下标会减1;往右边吃,当前怪兽下标不变,右边被吃的那只怪兽右边的所有怪兽下标都减1.     

代码:

#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;

#define ll long long
#define INF 0x3f3f3f3f
#define LL_INF 0x3f3f3f3f3f3f3f3f
#define MAX 550

int before[MAX], after[MAX]; //before存的是开吃之前的怪兽重量数组,after是目标的重量数组

void display(int now, int id, int l, int r, int diff)
{
	int acId = id;
	while (id > l && now > before[id-1]) { //先往左边吃
		now += before[id-1];
		printf("%d L\n", id-diff);
		id--;
	}
	while (acId < r && now > before[acId+1]) { //再往右边吃
		now += before[acId+1];
		printf("%d R\n", id-diff);
		acId++;
	}
	while (id > l && now > before[id-1]) { //最后看看能不能往左边吃,能就吃
		now += before[id-1];
		printf("%d L\n", id-diff);
		id--;
	}
}


int main()
{
	//freopen("debug\\in.txt", "r", stdin);
	//freopen("CON", "w", stdout);
	int i, j, k, n, m;
	int sum = 0;
	scanf("%d", &n);
	for (i = 1; i <= n; ++i) {
		scanf("%d", &before[i]);
		sum += before[i];
	}
	scanf("%d", &m);
	for (i = 1; i <= m; ++i) {
		scanf("%d", &after[i]);
		sum -= after[i];
	}
	if (sum) {  //前后两者的重量不守恒的话说明肯定不可能
		printf("NO\n");
		return 0;
	}
	int tmp = 0, last = 0, maxn = 0, max_index = 0, cnt = 1;
	pair<int, int> index[MAX]; //index[j]存的是after[j]对应的before区间的起始位置以及终止位置
	int maxn_index[MAX];	//maxn_index[j]存的是index[j]区间内的最大值,方便最后输出
	for (i = j = 1; i <= n && j <= m; ++i) { //开始匹配,从before中找寻一段区间[last, i]来组成after[j]
		tmp += before[i];
		if (before[i] > maxn) {
			maxn = before[i];
			max_index = i;
		}
		if (tmp == after[j]) {
			if (tmp / (i - last) == maxn && (i - last) > 1) { 
				printf("NO\n"); 	//在这一段区间内的重量都一样的,你吃不了我我也吃不了你,所以这种情况也是不可能的
				return 0;
			}
			j++;
			tmp = 0;
			maxn = 0;
			index[cnt] = make_pair(last + 1, i);
			maxn_index[cnt] = max_index;
			last = i;
			cnt++;
		}
		if (tmp > after[j]) {
			printf("NO\n"); //因为是连续的,所以一旦一段连续的区间无法组成目标重量,就说明不可能了
			return 0;
		}
	}
	if (i-1 != n || j-1 != m) {
		printf("NO\n"); //匹配了一遍发现多出来了几只怪兽或者缺了几只怪兽,那肯定也是不行的
		return 0;
	}
	int diff = 0;
	printf("YES\n");
	for (i = 1; i < cnt; ++i) {
		int id = maxn_index[i];
		int l = index[i].first, r = index[i].second;
		if (id == l) {
			while (before[id] == before[id+1] && id < r)
				id++;
		}
		int now = before[id];
		display(now, id, l, r, diff); //开吃
		diff += r - l;
	}
	return 0;
}

PS:这道题如果改成初始怪兽们可以随机吃呢?该怎么算?留给我思考。


D.Kostya the Sculptor

题目链接:

http://codeforces.com/contest/733/problem/D

题目大意:

给你一堆的长方体石头,取其中的两个拼成一个更大的长方体石头,求取哪两个石头使得这个更~大的长方体石头的内切球半径最大。

题解:

想要拼的起来,那么就得保证两个石头的至少一个面相同,也就是长、宽都相等。而且内切球的半径大小取决于长方体的最短边。

那么将所有长方体按最长边与次长边排个序,最长边与次长边相同的两个长方体来拼拼看,比较比较,就可以了。

一开始逗逼了,把长方体按长、宽、高相同数据不同分类分成六种长方体,排个序,然后同高同宽的两两拼一下,直接把复杂度搞成O(n!),弄了半天都没发现。其实只需要排个序,然后相邻两个试着拼一下就可以了(仍不理解为什么,留着以后看),复杂度O(nlogn),取决于排序算法的复杂度。

代码:

#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;

#define ll long long
#define INF 0x3f3f3f3f
#define LL_INF 0x3f3f3f3f3f3f3f3f
#define MAX 100100

typedef struct Rectangle
{
	int a, b, c; //a,b是最长边与次长边,c是最短边
	int index; //原来输入的顺序下标

	Rectangle() : a(0), b(0), c(0), index(0) {}
	Rectangle(int i, int j, int k, int l) : a(i), b(j), c(k), index(l) {}
	// bool operator < (const Rectangle &r) {
	// 	if (a != r.a)
	// 		return a < r.a;
	// 	else if (b != r.b)
	// 		return b < r.b;
	// 	else
	// 		return c < r.c;
	// } //写个关系运算符重载搞了半天没成功
}Rec;

Rec rect[MAX * 6];

void display(int n)
{
	for (int i = 1; i <= 2 * n; ++i)
		printf("%d %d %d %d\n", rect[i].a, rect[i].b, rect[i].c, rect[i].index);
}

bool cmp(Rec r1, Rec r2)
{
	if (r1.a != r2.a)
		return r1.a < r2.a;
	else if (r1.b != r2.b)
		return r1.b < r2.b;
	else
		return r1.c < r2.c;
} 

int main()
{
	//freopen("debug\\in.txt", "r", stdin);
	//freopen("CON", "w", stdout);
	int i, j, k;
	int n, maxn = 0, first = 0, second = 0; //maxn是最大的组合,first表示第一个石头的下标,second是第二个,可为0
	scanf("%d", &n);
	int a, b, c;
	for (i = 1; i <= n; ++i) {
		scanf("%d %d %d", &a, &b, &c);
		int l1, l2, l3;
		l1 = max(max(a, b), c);
		l3 = min(min(a, b), c);
		l2 = a + b + c - l1 - l3;
		rect[i] = Rectangle(l1, l2, l3, i);
		if (l3 > maxn) { //选一个石头的情况,选出最大的一个
			maxn = l3;
			first = i;
		}
	}
	sort(rect, rect + n, cmp);
	//display(n);
	int minw = 0;
	for (i = 1; i <= n; ++i) {
		if (rect[i].a == rect[i+1].a && rect[i].b == rect[i+1].b) { //因为排好序了所以相邻的组合一下试试看就可以了
			minw = min(min(rect[i].a, rect[i].b), rect[i].c + rect[i+1].c);
			if (minw > maxn) {
				maxn = minw;
				first = rect[i].index;
				second = rect[i+1].index;
			}
		}
	}
	if (second)
		printf("2\n%d %d\n", first, second);
	else
		printf("1\n%d\n", first);
	return 0;
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值