1 ABC404 A~E 题解

A

题面

给你一个长度介于 \(1\sim25\) 之间的字符串 \(S\) ,它由小写英文字母组成。
请输出一个没有出现在 \(S\) 中的小写英文字母。如果有多个,输出其中任意一个即可。


题解

直接用一个 \(vis[]\) 记录每个字母是否出现过即可

code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cstdio>

using namespace std;

namespace michaele {
	int n;
	bool vis[26];
	void main () {
		string s;
		cin >> s;
		for (int i = 0; i < (int)s.size(); i++) 
			vis[s[i] - 'a' + 1] = 1;
		for (int i = 1; i <= 26; i++) {
			if (!vis[i]) {
				printf ("%c", i + 'a' - 1);
				break;
			}
		}
		return;
	}
}
int main () {
	michaele :: main ();
	return 0;
}

B

题面

有两个网格 \(S\)\(T\) ,每个网格有 \(N\) 行和 \(N\) 列。让 \((i,j)\) 表示从上往下数第 \(i\) 行和从左往上数第 \(j\) 列的单元格。

网格 \(S\)\(T\) 中的每个单元格都被涂成白色或黑色。如果 \(S_{i,j}\) 为".",则 \(S\)\((i,j)\) 单元格为白色;如果 \(S_{i,j}\) 为 "#",则 \((i,j)\) 单元格为黑色。同样的情况也适用于 \(T\)

您可以按任意顺序执行以下两种类型的运算任意多次。求使网格 \(S\) 与网格 \(T\) 相同所需的最少运算次数。

  • 选择网格 \(S\) 中的一个单元格并改变其颜色。
  • 将整个网格 \(S\) 顺时针旋转 \(90\) 度。

\(1 \leq N \leq 100\)

题解

这题当时想了想,不太知道怎么优雅的做出来,后来直接暴力模拟了,看题解好像代码比较优雅,但思路差不多

如果不再旋转,那么当前 \(S\)\(T\) 中不同的方格的数量就是答案

如果旋转,那我们需要知道旋转后的每个点的坐标

image

不难发现,原本的 \((x,y)\) 变成 \((y,n+1-x)\) ,这样我们就可以实现旋转操作

code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cstdio>

using namespace std;

namespace wjl {
	
	const int N = 110;
	int n;
	int a[N][N][4];
	int b[N][N];
	char s[110];
	void main () {
		scanf ("%d", &n);
		for (int i = 1; i <= n; i++) {
			scanf ("%s", s + 1);
			for (int j = 1; j <= n; j++) {
				a[i][j][0] = s[j] == '.' ? 0 : 1;
			}
		}
		for (int i = 1; i <= n; i++) {
			scanf ("%s", s + 1);
			for (int j = 1; j <= n; j++) {
				b[i][j] = s[j] == '.' ? 0 : 1;
			}
		}
		for (int t = 1; t <= 3; t++) {
			for (int i = 1; i <= n; i++) {
				for (int j = 1; j <= n; j++) {
					a[j][n + 1 - i][t] = a[i][j][t - 1];	
				}
			}
		}	
		int ans[4] = {0, 1, 2, 3};
		for (int t = 0; t <= 3; t++) {
			for (int i = 1; i <= n; i++) {
				for (int j = 1; j <= n; j++) {
					if (a[i][j][t] != b[i][j]) {
						ans[t]++;
					}
				}
			}
		}
		sort (ans, ans + 4);
		printf ("%d\n", ans[0]);
		return;
	}
}

int main () {
	wjl :: main ();
	return 0;
}

C

题面

给你一个简单的无向图,图中有 \(N\) 个顶点和 \(M\) 条边。顶点编号为 \(1,2,\dots,N\) ,边编号为 \(1,2,\dots,M\) 。边 \(i\) 连接顶点 \(A _{i}\)\(B _{i}\) 。(不存在重边和自环)

循环图的定义 如果存在 \((1,2,\dots,N)\)\((v_{1},v_{2}\dots ,v_{N})\) 排列,且顶点标记为 \(1,2,\dots,N\) ,则 \(N\) 顶点图是循环图:

  • 对于每个 \(i = 1,2,\dots,N-1\) ,顶点 \(v_{i}\)\(v_{i+1}\) 之间有一条边。
  • 顶点 \(v _{N}\)\(v _{1}\) 之间有一条边。
  • 不存在其他边。

请判断该图是否为循环图。

  • \(3 \le N \le 2\times 10^5\)
  • \(0 \le M \le 2\times 10^5\)
  • \(1 \le A_i, B_i \le N\)

题解

判断是否满足以下条件

  • \(n\) 个点 \(n\) 条边
  • 每个点连 \(2\) 条边
  • 整个图联通(可用DFS或并查集)

code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <vector>

using namespace std;

namespace michaele {
	
	const int N = 2e5 + 10;
	
	int n, m;
	vector <vector<int>> e(N);
	bool vis[N];
	bool ok = 1;
	void dfs (int x, int fa) {
		if (vis[x]) return;
		vis[x] = 1;
		if ((int)e[x].size() != 2) {
			ok = 0;
			return;
		}
		for (auto y : e[x]) {
			if (y == fa) continue;
			dfs (y, x);
		}
	}
	void main () {
		scanf ("%d%d", &n, &m);
		if (n != m) {
			printf ("No\n");
			return;
		}
		int x, y;
		for (int i = 1; i <= m; i++) {
			scanf ("%d%d", &x, &y);
			e[x].push_back (y);
			e[y].push_back (x);
			if ((int)e[x].size() > 2 || (int)e[y].size() > 2) {
				printf ("No\n");
				return;
			}
		}
		dfs (1, 0);
		if (!ok) {
			printf ("No\n");
			return;
		}
		for (int i = 1; i <= n; i++) {
			if (!vis[i]) {
				printf ("No\n");
				return;
			}
		}
		printf ("Yes\n");
		return;
	}
}

int main () {
	michaele :: main ();
	return 0;
}

D

题面

\(AT\) 国家有 \(N\) 个动物园,编号为 \(1\)\(N\) 。动物园 \(i\) 的门票是 \(C_i\) 元。

小乐喜欢 \(M\) 种动物,动物 \(1,\dots,M\)
动物园 \(K_i\) ,即动物园 \(A_{i,1},\dots,A_{i,K_i}\) 可以看到动物 \(i\)

求观看所有 \(M\) 种动物每种至少两次最少多少钱。
如果多次游览同一动物园,则每次游览都被视为观赏一次动物。

  • \(1 \le N \le 10\)
  • \(1 \le M \le 100\)

题解

对于每个动物园来说,小乐最多去两次,因为第三次对答案没有任何贡献

又因为 \(1 \leq N \leq 10\) ,所以我们可以直接暴搜,枚举每个动物园去 \(0,1,2\)
时间复杂度 \(O(3^{N}NM)\)

code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <vector>

using namespace std;

namespace michaele {
	typedef long long ll;
	
	const int N = 110;
	
	int n, m;
	vector <vector<int>> e(N);
	ll c[N], ans = 2e18;
	int ct[N];
	
	//i 表示当前所处的动物园, cost代表到现在要花多少钱
	void dfs (int i, ll cost) {
		if (i > n) {
			for (int j = 1; j <= m; j++) {
				if (ct[j] < 2) return;
			}
			ans = min (ans, cost);
			return;
		}
		//不看
		dfs (i + 1, cost);
		//看一次
		for (auto j : e[i]) ct[j]++;
		dfs (i + 1, cost + c[i]);
		for (auto j : e[i]) ct[j]--;
		//看两次
		for (auto j : e[i]) ct[j] += 2;
		dfs (i + 1, cost + c[i] * 2);
		for (auto j : e[i]) ct[j] -= 2;
	}
	
	void main () {
		scanf ("%d%d", &n, &m);
		for (int i = 1; i <= n; i++) 
			scanf ("%lld", &c[i]);
		for (int i = 1; i <= m; i++) {
			int x;
			scanf ("%d", &x);
			for (int j = 1; j <= x; j++) {
				int xx;
				scanf ("%d", &xx);
				e[xx].push_back (i);
			}
		}
		dfs (1, 0);
		printf ("%lld\n", ans);
		return;
	}
}

int main () {
	michaele :: main ();
	return 0;
}

E

题面

\(N\) 个碗排成一排,从左到右编号为 \(0,1,\dots,N-1\)
每个碗 \(i\) ( \(1\le i\le N-1\) ) 上都写着一个整数 \(C_i\) ,最初碗里有 \(A_i\) 粒豆子。
\(0\) 上没有写任何整数,最初也没有豆子。

下面的操作可以重复任意多次:

  • 选择一个碗 \(i\) ( \(1\le i\le N-1\) ),从中取出一颗或多颗豆子。
  • 将取出的豆子在碗 \(i-C_i,i-C_i+1,\dots,i-1\) 中自由分配。
    • 从形式上看,当你取出 \(k\) 粒豆子时,你必须将总共 \(k\) 粒豆子放入碗 \(i-C_i,i-C_i+1,\dots,i-1\) 中,你可以选择每只碗中放入多少粒豆子。

求将所有豆子放入碗 \(0\) 所需的最少操作次数。

题解

这题要求最小操作次数,考虑不同的操作顺序对答案的影响

  • 先动左边,再动右边,那么右边的移动到左边后要额外消耗操作次数
  • 先动右边,再动左边,将左边原有的和右边移过来的一起动,即可节省操作次数

那么我们从右到左将每个碗中的豆子都移到更左边的碗中,那么这就是最优的答案

这里用最短路的做法,枚举每个碗

  • 从右到左枚举每个可以转移的碗,如果这个碗中有豆子,那么连一条左边到右边的边,退出循环
  • 如果没有找到有豆子的碗,那么向每个可转移碗都连一条左边到右边的边

最后以 \(0\) 为起点跑 \(dijkstra\) 即可
最后答案就是最右边的第一个有豆子的碗的 \(d[x]\)

时间复杂度 \(O(N^{2})\) ,瓶颈在建边

code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <queue>

using namespace std;

namespace wjl {
	
	const int N = 2e3 + 10;
	
	int n;
	int a[N], c[N], d[N];
	vector <vector<int>> e (N);
	bool vis[N];
	
	void dj (int s) {
		memset (d, 0x3f, sizeof d);
		priority_queue<pair<int, int>> q;
		d[s] = 0;
		q.push({0, s});
		while (q.size()) {
			int x = q.top().second; q.pop();
			if (vis[x]) continue;
			vis[x] = 1;
			for (auto y : e[x]) {
				if (d[y] > d[x] + 1) {
					d[y] = d[x] + 1;
					q.push({-d[y], y});
				}
			}
		}
	}
	
	void main () {
		scanf ("%d", &n);
		for (int i = 1; i < n; i++) 
			scanf ("%d", &c[i]);
		for (int i = 1; i < n; i++) 
			scanf ("%d", &a[i]);
		for (int i = 1; i < n; i++) {
			bool fn = 0;
			for (int j = i - 1; j >= i - c[i]; j--) {
				if (a[j]) {
					e[j].push_back (i);
					fn = 1;
					break;
				}
			}
			if (!fn) {
				for (int j = i - c[i]; j <= i - 1; j++)
					e[j].push_back (i);
			}
		}
		dj (0);
		for (int i = n - 1; i >= 1; i--) {
			if (a[i]) {
				printf ("%d\n", d[i]);
				return ;
			}
		}
		return;
	}
}

int main () {
	wjl :: main ();
	return 0;
}
posted @ 2025-05-07 16:34  michaele  阅读(50)  评论(0)    收藏  举报