230513 第二次周考

第一次周考被菌哥哥(白眼)吃了


A. 行走的机器人

http://222.180.160.110:1024/contest/3583/problem/1

不难想到用 std::next_permutation 解决方向问题,然后模拟走路过程判定。

复杂度 \(\mathcal O(24n^2)\),其中 \(24=A_4^4\)。赛时耗时 14min。

namespace XSC062 {
using namespace fastIO;
const int maxn = 55;
const int maxm = 105;
const int fx[] = { 1, -1, 0, 0 };
const int fy[] = { 0, 0, 1, -1 };
int a[maxn];
char s[maxm];
char t[maxn][maxn];
int n, m, l, x, y, sx, sy, ex, ey, res;
int main() {
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; ++i) {
		scanf("%s", t[i] + 1);
		for (int j = 1; j <= m; ++j) {
			if (t[i][j] == 'S')
				sx = i, sy = j;
			if (t[i][j] == 'E')
				ex = i, ey = j;
		}
	}
	scanf("%s", s + 1);
	l = strlen(s + 1);
	for (int i = 0; i < 4; ++i)
		a[i] = i;
	for (int i = 1; i <= 24; ++i) {
		x = sx, y = sy;
		for (int j = 1; j <= l; ++j) {
			x += fx[a[s[j] - '0']];
			y += fy[a[s[j] - '0']];
			if (x < 1 || y < 1 || x > n ||
					y > m || t[x][y] == '#')
				goto EndLoop;
			if (x == ex && y == ey) {
				++res;
				goto EndLoop;
			}
		}
		EndLoop: ;
		std::next_permutation(a, a + 4);
	}
	printf("%d", res);
	return 0;
}
} // namespace XSC062

B. 多米诺骨牌涂色

http://222.180.160.110:1024/contest/3583/problem/2

很明显的一道小 DP。

我一开始定义的状态是 \(f_{i, j, k}\) 表示在第 \((i, j)\) 格涂 \(k\) 颜色的方案数。

但状态越多,求解会出现的 bug 就越多。我在无数次调试后发现颜色其实是可轮换的,我们只需要将状态简化为 \(f_{i, j}\) 表示 \((i, j)\) 格确定 某种颜色。所以转移时不用讨论上一列的排列(即认定上一列为某种颜色,不作枚举),只讨论当前列的排列。

首先明白一点:任意两块横向长方形不可能构成 Z 字形的摆放。这一点很好证明,我们后面的分类讨论和状态转移也会因为这个性质简单很多。

对于从第二列开始的任意一列,该列内的情况只有可能是:

  • 该列为一块竖向排列的长方形

    • 上一列也为一块竖向排列的长方形

      假设上一列的颜色是 \(a\),那么这一列只能是 \(b\)\(c\)

      \(f_{1, i}, f_{2, i} = 2\times f_{1, i - 1}\)

      因为 \(f_{1, i - 1}\)\(f_{2, i - 1}\) 也是轮换的(因为不存在 Z 形),所以直接用 \(f_{1, i - 1}\) 代替即可。

    • 上一列是两块横向排列的长方形的结尾

      假设上一列的两种颜色从上到下为 \((a, b)\),那么这一列只能是 \(c\)

      \(f_{1, i}, f_{2, i} = f_{1, i - 1}\)

  • 该列为两块横向排列的长方形

    • 该列为结尾的两块

      继承开头的状态即可。即 \(f_{1, i} = f_{1, i - 1},f_{2, i} = f_{2, i - 1}\)

    • 该列为开头的两块

      • 上一列为一块竖向长方形

        假设上一列颜色为 \(a\),那么这一列上下两块的颜色排列既可以是 \(b, c\),也可以是 \(c, b\)

        \(f_{1, i}, f_{2, i} = 2\times f_{1, i - 1}\)

      • 上一列为两块横向长方形的末尾

        假设上一列从上到下为 \((a, b)\) 两种颜色,那么这一列从上到下可以是 \((b, a)\)\((b, c)\)\((c, a)\) 三种情况。

        \(f_{1, i}, f_{2, i} = 3\times f_{1, i - 1}\)

最后注意初始化。

  • 当第一列为一块竖向长方形时

    该列没有任何限制,所以 \(f_{1, 1}, f_{2, 1} = 3\)

  • 当第一列为两块横向长方形的开头时

    由排列组合得 \(f_{1, 1}, f_{2, 1} = A_3^2 = 6\)

时间复杂度 \(\mathcal O(n)\)

看了题解过后才意识到,原来状态还可以再简化!因为 \(f_{1, i}\)\(f_{2, i}\) 始终是相等的。但是我懒得改了!

赛时耗时 1h8min。

#define int long long
namespace XSC062 {
using namespace fastIO;
const int maxn = 55;
const int mod = 1e9 + 7;
int n;
int f[5][maxn];
char t[5][maxn];
int main() {
	scanf("%lld", &n);
	for (int i = 1; i <= 2; ++i)
		scanf("%s", t[i] + 1);
	if (t[2][1] == t[1][1])
		f[1][1] = f[2][1] = 3;
	else f[1][1] = f[2][1] = 6;
	for (int i = 2; i <= n; ++i) {
		// 该列为竖向 
		if (t[1][i] == t[2][i]) {
			// 上一列也为竖向 
			if (t[1][i - 1] == t[2][i - 1]) {
				f[1][i] = f[2][i] =
					(2 * f[1][i - 1]) % mod;
			}
			// 上一列为横向 
			else f[1][i] = f[2][i] = f[1][i - 1];
		}
		// 该列为横向 
		else {
			// 该列为结尾 
			if (t[1][i] == t[1][i - 1]) {
				f[1][i] = f[1][i - 1];
				f[2][i] = f[2][i - 1];
			}
			// 该列为开头 
			else {
				// 上一列为竖向 
				if (t[1][i - 1] == t[2][i - 1]) {
					f[1][i] = f[2][i] =
						(2 * f[1][i - 1]) % mod;
				}
				// 上一列也为横向
				else {
					f[1][i] = f[2][i] = 
						(3 * f[1][i - 1]) % mod;
				} 
			}
		}
	}
	print(f[2][n]);
	return 0;
}
} // namespace XSC062
#undef int 

C. 最小得分

http://222.180.160.110:1024/contest/3583/problem/3

如果数组里没有 0,皆大欢喜,输出 0。

如果数组里有 0,那就隔一个非零数插一个 0,答案肯定是 0。

如果按上面的方法插完了还有 0,那就说明相邻的 0 是一定会存在的,那么就可以把非 0 数全部堆在一起,那么次小的至少都是 1 + 1 = 2。此时输出最小值和次小值之间的 1 即可。

但有一种例外,那就是整个数组除了 0 就只有 1,那么就说明一定会有一个 1 紧挨着 0,那么我们还是按照隔一个插一个的策略,答案就是 2。

全是 0 也应该输出 1,但是这种情况会被我们之前的讨论涵盖。

namespace XSC062 {
using namespace fastIO;
const int maxn = 2e5 + 5;
int a[maxn];
int T, n, cnt0, mx;
inline int max(int x, int y) {
	return x > y ? x : y;
}
int main() {
	read(T);
	while (T--) {
		read(n);
		cnt0 = mx = 0;
		for (int i = 1; i <= n; ++i) {
			read(a[i]), cnt0 += (!a[i]);
			mx = max(mx, a[i]);
		} 
		if (cnt0 <= n - cnt0 + 1)
			print(0, '\n');
		else if (mx == 1)
			print(2, '\n');
		else print(1, '\n');
	}
	return 0;
}
} // namespace XSC062
posted @ 2023-05-13 15:28  XSC062  阅读(29)  评论(0编辑  收藏  举报