2024“钉耙编程”中国大学生算法设计超级联赛(6)

传送门

1001 造花(简单版)

如果删去一个点后能分成两个菊花图,那么删去的点度数一定为2。如果能成功分成两个菊花图,那么原来的图中最多只有 5 个度数大于等于 2 的点(被删去的点、与被删去的点直接相连的两个点、两个菊花图中与剩余点直接相连的点)。

所以只需要检查每一个度数为 2 的点删去后是否能构成两个菊花图,检查一次的时间复杂度为 O(n),最多不会超过 5 次检查,所以总时间复杂度也是 O(n)

#include <bits/stdc++.h>
using namespace std;

struct dsu {
	vector<int> v;
	dsu(int n): v(n + 1) {
		for (int i = 0; i <= n; i++) v[i] = i;
	}
	int find(int x) { return x == v[x] ? x : v[x] = find(v[x]); }
	void unit(int x, int y) { v[find(x)] = find(y); }
};

inline int read() {
	int x = 0, f = 1; char c = getchar();
	while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }
	while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return x * f;
}

bool solve() {
	int n = read();
	vector<set<int>> e(n + 1);
	for (int i = 1; i < n; i++) {
		int u = read(), v = read();
		e[u].insert(v), e[v].insert(u);
	}
	auto check = [&](int t) {
		bool flag = true;
		dsu d(n);
		for (int i = 1; i <= n; i++)
			for (int j : e[i])
				if (i != t && j != t) d.unit(i, j);
        // 将一个图中的点用并查集连在一起
		map<int, vector<int>> mp;
		for (int i = 1; i <= n; i++)
			if (i != t) mp[d.find(i)].push_back(i);
        // 将一个图中的所有结点编号全部存到一起
		for (int v : e[t]) e[v].erase(t); // 删除节点 t 后再进行判断
		for (auto [a, b] : mp) {
			int cnt = 0;
			for (int x : b) if (e[x].size() == 1) cnt++;
			if (b.size() - cnt > 1) flag = false;
            // 最多有一个节点度数不为 1
		}
		for (int v : e[t]) e[v].insert(t); // 重新加入节点 t
		return flag;
	}; // 检查是否构成两个菊花图
	int cnt = 0;
	for (int i = 1; i <= n; i++) {
		if (e[i].size() == 1) continue;
		if (++cnt > 5) break; // 超过 5 个度数大于等于 2 的节点
		if (e[i].size() == 2 && check(i)) return true;
	}
	return false;
}

int main() {
	int t = read();
	while (t--) {
		puts(solve() ? "Yes" : "No");
	}
	return 0;
}

1003 飞车狂飙

假设飞车从 (0, 0) 出发,初始时方向朝右,用一个 set 存经过的点的坐标,判断是否有重叠,最后看是否成环(回到起点且方向相同)。

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10;
int t, n;
char str[N];
set<pair<int, int>> s;
const int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
// 分别表示向右、上、左、下方向移动一个单位

inline int read() {
	int x = 0, f = 1; char c = getchar();
	while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }
	while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return x * f;
}

int main() {
	t = read();
	while (t--) {
		n = read();
		scanf("%s", str);
		s.clear();
		int d = 0; // 用来记录方向
		s.insert({0, 0});
		int x = 0, y = 0, flag = 0;
		for (int i = 0; i < n; i++) {
			if (str[i] == 'L') d = (d + 1) % 4;
			else if (str[i] == 'R') d = (d + 3) % 4;
			int xx = x + dx[d], yy = y + dy[d];
			if (i != n - 1 && s.count({xx, yy})) flag = 1; // 之前已经经过了这个点,形成了重叠
			else s.insert({xx, yy});
			x = xx, y = yy;
		}
		if (flag) {
			puts("-1");
		} else {
			if (x == 0 && y == 0 && d == 0) puts("1"); // 回到原点且方向相同
			else puts("0");
		}
	}
	return 0;
}

1004 不醒人室

按照题意模拟:遍历每一个休息的时间,更新不困的状态的时间,判断是否会在课上休息或者课上处于很困的状态。

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10;
int t, n, m;
struct { int b, e; } cla[N];
struct { int s, t; } sle[N];

inline int read() {
	int x = 0, f = 1; char c = getchar();
	while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }
	while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return x * f;
}

void solve() {
	n = read(), m = read();
	cla[0] = {0, 0};
	for (int i = 1; i <= n; i++) cla[i].b = read(), cla[i].e = read();
	for (int i = 1; i <= m; i++) sle[i].s = read(), sle[i].t = read();
	int ok = 0, p = 1;
	for (int i = 1; i <= m; i++) {
		if (cla[p - 1].e <= sle[i].s && sle[i].t <= cla[p].b) {
			ok = max(ok, sle[i].t + 2 * (sle[i].t - sle[i].s)); // 更新清醒时间
		} else {
			puts("No");
			return;
		}
		while (p + 1 <= n && ok >= cla[p].e && cla[p].b < sle[i + 1].t) p++;
	}
	puts("Yes");
}

int main() {
	t = read();
	while (t--) solve();
	return 0;
}

1005 交通管控

根据 k 的范围,我们可以想到状压dp。将每一种字符串转换成对应的一个整数(总共不超过 3^k 种)。从头到尾遍历每一个字符串 s,根据字符串 s 对现有的状态进行修改并且更新各个状态的数量。

#include <stdio.h>
#include <vector>
#include <functional>
using namespace std;

const int pow3[11] = {1, 3, 9, 27, 81, 243, 729, 2187, 6561, 19683, 59049};
int M;
char s[11];

struct modint {
	int x;
    bool is_zero; // 记录个数是真的为 0 还是是模数的倍数
	modint(): x(0), is_zero(true) {}
	modint(int _x): x(_x), is_zero(x == 0){}
	void operator += (const modint & a) {
		if (a.is_zero) return;
		x += a.x;
		if (x >= M) x -= M;
		is_zero = false;
		return;
	}
};

inline int read() {
	int x = 0, f = 1; char c = getchar();
	while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }
	while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return x * f;
}

int main() {
	int t = 1;
	t = read();
	while (t--) {
		int n, k;
		n = read(), k = read(), M = read();
		vector<modint> dp(pow3[k]);
		dp[0] = modint(1);
		for (int i = 0; i < n; i++) {
			scanf("%s", s);
			function<int(int)> calc = [&](int x) {
				int num = 0;
				for (int i = 0; i < k; i++) {
					int tt = (x / pow3[k - i - 1]) % 3;
					if (s[i] == '+') tt = (tt + 1) % 3;
					else if (s[i] == '-') tt = (tt + 2) % 3;
					num = num * 3 + tt;
				} // 将字符串转换成整数
				return num;
			};
			vector<modint> tmp = dp;
			for (int j = 0; j < pow3[k]; j++) tmp[calc(j)] += dp[j];
			dp = tmp;
		}
		for (int i = 0; i < pow3[k]; i++) {
			if (!dp[i].is_zero) {
				for (int j = k - 1; j >= 0; j--) {
					int tt = (i / pow3[j]) % 3;
					putchar(tt + 'A');
				} // 将整数转换成字符串
				printf(" %d\n", dp[i].x);
			}
		}
	}
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值