ABC307 CDE Solution - By TB_TOP

07yintaobo 2023-07-05 18:01:39 2023-07-05 18:02:29 4

C

题意

有两个由 组成的矩阵 ,并给出矩阵 , 判断是否有一种方式,有两个分别来自 的子矩阵,同时满足以下情况:

  • 其中包含了 的所有
  • 在某种排列方式下,二者的所有 可以恰好放在 的每一个 上,注意,两个子矩阵可以重叠,但 的每一个 上必须有来自子矩阵的 覆盖

思路

这一题的数据范围较小,故我们只需对其进行暴力枚举即可,本人的枚举思路为:

固定矩阵 不动,并使 与之重合

然后枚举 的平移距离(包括上下和左右)。由于一张图的大小不超过 , 故容易证得 的平移距离一定不大于 , 可能还能更小,但笔者未进行严谨证明,相信各位看官一定有更好的想法。

接下来的难点就在于如何判断平移距离是否合法,笔者便简述一下

首先,我们现对 和平移过后的 进行重合操作,由于平移可能向上向左,故需对其进行偏移以避免越界,如下

void merge(int x, int y) {
	memset(b, 0, sizeof(b)); 
	for (int i = 1; i <= n[1] + n[2]; i++) {
		for (int j = 1; j <= m[1] + m[2]; j++) {
			if (a[1][i][j]) b[i + p][j + p] = 1;
			if (a[2][i][j]) b[i + x + p][j + y + p] = 1;
		} 
	}
}

然后我们便通过不断调整 的位置,一一比对,判断其是否与 完全重合,注意在判断时,也要判断该放置方式是否包含了全部的 #, 为方便操作,笔者将其转换为 二进制储存

bool check(int x, int y){
	merge(x, y);
	int cnt = 0;
	for (int i = 0; i <= 100; i++) 
		for (int j = 0; j <= 100; j++) 
			if (b[i][j]) {
				cnt ++;

	for (int i = 1; i <= n[1] + 20; i++) 
		for (int j = 1; j <= m[1] + 20; j++) {
			int flag = 0, tot = 1, t = 0;
			for (int xx = 1; xx <= n[3] + 10; xx++) {
				for (int yy = 1; yy <= m[3] + 10; yy++) {
					if (a[3][xx][yy]) t++;
					if (a[3][xx][yy] != b[i + xx - 1][j + yy - 1]) {
						flag = 1;
						break;
					}
				}
				if (flag) break;
			} 
			if (!flag && cnt == t) {
				return 1;
			}
		}
	return 0;
}

总代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 200, p = 20;
int n[4], m[4];
int a[4][N][N];
int b[N][N];
void merge(int x, int y) {
	memset(b, 0, sizeof(b)); 
	for (int i = 1; i <= n[1] + n[2]; i++) {
		for (int j = 1; j <= m[1] + m[2]; j++) {
			if (a[1][i][j]) b[i + p][j + p] = 1;
			if (a[2][i][j]) b[i + x + p][j + y + p] = 1;
		} 
	}
}
bool check(int x, int y){
	merge(x, y);
	int cnt = 0;
	for (int i = 0; i <= 100; i++) 
		for (int j = 0; j <= 100; j++) 
			if (b[i][j]) {
				cnt ++;

	for (int i = 1; i <= n[1] + 20; i++) 
		for (int j = 1; j <= m[1] + 20; j++) {
			int flag = 0, tot = 1, t = 0;
			for (int xx = 1; xx <= n[3] + 10; xx++) {
				for (int yy = 1; yy <= m[3] + 10; yy++) {
					if (a[3][xx][yy]) t++;
					if (a[3][xx][yy] != b[i + xx - 1][j + yy - 1]) {
						flag = 1;
						break;
					}
				}
				if (flag) break;
			} 
			if (!flag && cnt == t) {
				return 1;
			}
		}
	return 0;
}
signed main(){
	for (int p = 1; p <= 3; p++) {
		cin >> n[p] >> m[p];
		for (int i = 1; i <= n[p]; i++) 
			for (int j = 1; j <= m[p]; j++) {
				char c;
				cin >> c;
				if (c == '#') a[p][i][j] = 1;
			}
	}
//	cout << check(0, 0) << endl; 
	for (int x = -20; x <= 20; x++) {
		for (int y = -20; y <= 20; y++) {
			if (check(x, y)) {
				cout << "Yes" << endl;
				return 0;
			}
		}
	}
	cout << "No" << endl;
	return 0;
}

总结

这一题实际上是对选手的代码能力,并不包含过多的高级算法,是一道不错的枚举题

D

前置知识

  • 栈的概念
  • STL 中的栈操作

思路

看到括号的配对,我们不难想到栈,同时,许多题解中也选用了栈,但为什么呢,这里我们进行一个分析

因为在这道题中,每碰到一个右括号,我们都需要需找到距离这一括号最近的左括号,而离其最近的左括号必然是在此之前最后输出的括号,由此,我们发现其满足后进先出的特点,故我们果断选用栈,同时,在 C++ 中,STL 有着天然的栈,我们又多了一个选栈的理由,因此,我们选用栈进行操作

代码实现

我们只需在读到一个右括号时,用栈找到最近的左括号,并将在此之后的字符一一弹出即可,代码如下:

#include<bits/stdc++.h>
#define int long long
using namespace std;
string s;
int n;
int tot = 0;
stack <char> sta;
signed main(){
	cin >> n;
	cin >> s;
	s = ' ' + s;
	for (int i = 1; i <= n; i++) {
		if (s[i] == '(') {
			tot++;
		else if (s[i] == ')') 
			if (tot) {
				while(sta.top() != '(') sta.pop();
				sta.pop();
				tot--;
				continue;
			}
		sta.push(s[i]);
	}
	string s = "";
	while (!sta.empty()) {
		s = sta.top() + s;
		sta.pop();
	}
	cout << s << endl;
	return 0;
}

E

题意

给定一个长度为 的环,每个位置可以填 ,求有多少种方案,满足相邻位置颜色不相同。

思路

无疑这是一道递推,故我们只需找到递推式即可,首先,对于 , 我们将其定义为,在长度为 的环中的填色方案总数,则对于 我们即可以从前面所得的 的方案数中递推得到,递推式分为两部分:

1.将一个首尾颜色相同的环的末端加入一个与首尾不同的色块,但我们知道,我们只有首尾颜色不同的方案数,故首尾相同的 可由 得到,因为若要首尾相同,只需在末尾加上一个与首位相同的数即可,故第一个式子得到了:

2.将一个本身合法的环加入一个与首尾均不同的色块,即:

故总递推式为:

注意

长度为 需预处理,因为在这一情况会存在越界现象,同时,也会影响答案合法性

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 10, mod = 998244353;
int n, m;
int f[N];
signed main(){
	cin >> n >> m;
	f[1] = m % mod;
	f[2] = m * (m - 1) % mod;
	f[3] = m * (m - 1) % mod * (m - 2) % mod;
	for (int i = 4; i <= n; i++) 
		f[i] = (f[i - 1] * (m - 2) + f[i - 2] * (m - 1)) % mod;	
	cout << f[n] << endl;
	return 0;
}
{{ vote && vote.total.up }}