usaco铜组【2023.1月赛】

1.29,第一次打usaco,成功晋级

1.选领袖

题目大意:
在这里插入图片描述

题目思路:

首先想到的是枚举两头牛但显然是不可行的,所以尝试用
O(n)或者O(nlogn)来解决,这两个复杂度分别代表用O(n)
来枚举第一个领袖牛,然后O(1)求出第二个领袖牛的方案
数,O(nlogn)也是用O(n)枚举第一个领袖牛,然后用
O(logn)来求出第二个领袖牛的方案数。考虑一下O(n),首
先第一头领袖牛有可能是第一种情况:一种品种的领袖也就
是统领一种品种。或者是第二种:领导领袖牛2,满足这几种
情况中的一种就有资格成为第一个领袖牛。第一个很好判
断,我们可以在一开始用Gx和Hx来代表领导G品种和H品种的
统领牛编号,所以第一种情况的判断就是判断是否为Gx或者
Hx。第二种情况,首先可以想到第二头领袖牛肯定是Gx或者
Hx了,所以说看一看当前这头牛是否领导了Gx或者Hx即可。
接下来需要想:假设我们现在已经找到了一个有资格成为领
袖牛1的牛,然后接下来需要求出领袖牛2的方案数。领袖牛
2有可能是Gx或者Hx,所以两个if判断一下即可。

代码(有点丑,见谅):

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10, INF = 0x3f3f3f3f;
int n, e[N];
char cow[N];
int Gl = INF, Gr = -INF, Hl = INF, Hr = -INF;
int Gx, Hx;
int main () {
	scanf ("%d", &n);
	scanf ("%s", cow + 1);
	for (int i = 1; i <= n; i ++ )
		scanf ("%d", &e[i]);
	for (int i = 1; i <= n; i ++ )
		if (cow[i] == 'G') {
			Gl = min (Gl, i);
			Gr = max (Gr, i);
		} else {
			Hl = min (Hl, i);
			Hr = max (Hr, i);
		}
	int res = 0;
	for (int i = 1; i <= n; i ++ )
		if (cow[i] == 'G') {
			if (i <= Gl && e[i] >= Gr)
				Gx = i;
		} else {
			if (i <= Hl && e[i] >= Hr)
				Hx = i;
		}
	for (int i = 1; i < n; i ++ )
		if (i == Gx || i == Hx || (i <= Gx && e[i] >= Gx) || (i <= Hx && e[i] >= Hx)) {
			if (Gx >= i + 1) res ++;
			if (Hx >= i + 1) res ++;
		}
	printf ("%d", res);
	return 0;
}

2.选空调

题目大意:

在这里插入图片描述

题目思路:

这道题的重点也就是空调的开关情况,只要知道了空调的开关情况,花费和具体的降温情况都可以求出,所以很明显可以dfs状态。dfs的状态就是dfs(u,cost)用u表示当前考虑了前u个空调,花费为cost的情况,然后用一个数组来计当前情况下每一个仓库中降温的情况。所以边界就是u>n时就判断现在的情况是否满足每一头奶牛的要求,如果满足了就将cost取min。如果没有满足u>n的情况就要继续dfs,有两种情况:开空调u、不开空调u。不开空调u的情况就是dfs(u+1,cost)也就是cost没有花费,开始考虑下一个空调的开关情况。开的话我们需要将这个空调所覆盖的范围全都降温,也就是更新一下我们前面记录降温情况的数组。这样的话时间复杂度还算比较优秀,但是还是比较危险,考虑优化。O(100)暂时没有什么可以优化的,所以考虑优化两种空调开关情况的优化。这个部分的时间都再更新数组了,所以我们尝试用O(1)来更新,可以发现这里是修改数组中连续的一段,这样就可以用差分来做,检查的时候就用前缀和来做就行了。

代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 25, INF = 0x3f3f3f3f;
int n, m, c_[110], belong[110];
struct Cow {
	int s, t, c;
} c[N];
struct Air {
	int s, t, p, c;
} r[N];
int res = INF;
void dfs (int u, int cost) {
	if (u > m) {
		bool F = true;
		int pre = 0;
		for (int i = 1; i <= 100; i ++ ) {
			pre += c_[i];
			if (belong[i] && pre < c[belong[i]].c)
				F = false;
		}
		if (F) res = min (res, cost);
		return;
	}
	c_[r[u].s] += r[u].p; c_[r[u].t + 1] -= r[u].p;
	dfs (u + 1, cost + r[u].c);
	c_[r[u].s] -= r[u].p; c_[r[u].t + 1] += r[u].p;
	dfs (u + 1, cost);
	return;
}
int main () {
	scanf ("%d%d", &n, &m);
	for (int i = 1; i <= n; i ++ )
		scanf ("%d%d%d", &c[i].s, &c[i].t, &c[i].c);
	for (int i = 1; i <= n; i ++ ) {
		for (int j = c[i].s; j <= c[i].t; j ++ )
			belong[j] = i;
	}
	for (int i = 1; i <= m; i ++ )
		scanf ("%d%d%d%d", &r[i].s, &r[i].t, &r[i].p, &r[i].c);
	dfs (1, 0);
	printf ("%d", res);
	return 0;
}

3.改字符

题目大意

在这里插入图片描述

题目思路

我感觉这题是最简单的题???
因为修改操作只能修改头和尾,所以可以想到肯定是先用删除操作将字符串的长度减到3,然后再通过修改来将长度尾3的字符串修改尾MOO,这里面中间位置的字符是不能改的,所以需要判断一下,这样就很简单了,枚举剩下的长度为3的字符串就行了,最后的答案也就是删除操作的操作数str.size()-3加上修改操作的最小值也就是前面枚举字符串的修改操作的最小值。

代码:

#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
int main () {
	int Q; scanf ("%d", &Q);
	while (Q -- ) {
		string s; cin >> s;
		if (s == "MOO") {
			printf ("0\n");
			continue;
		}
		if (s.size () < 3) {
			printf ("-1\n");
			continue;
		}
		int t = s.size () - 3, f = INF;
		for (int i = 0; i < s.size (); i ++ ) if (i + 2 < s.size ()) {
			char ch1 = s[i], ch2 = s[i + 2];
			int tmp = 0;
			if (s[i + 1] == 'O') {
				if (ch1 == 'O') tmp ++;
				if (ch2 == 'M') tmp ++;
				f = min (f, tmp);
			}
		}
		if (f == INF) printf ("-1\n");
		else printf ("%d\n", t + f);
	}
	return 0;
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

{∞}

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值