A 字符串
思路:模拟题意。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(0); cin.tie(0), cout.tie(0);
string a;
cin >> a;
cout << a.substr(1, a.size() - 1) << a[0];
return 0;
}
B 能看见几个?
思路:模拟题意。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(0); cin.tie(0), cout.tie(0);
int n, m, x, y;
cin >> n >> m >> x >> y;
x--, y--;
vector<string>a(n);
for (int i = 0; i < n; i++)cin >> a[i];
int res = 0;
for (int i = x; i >= 0 && a[i][y] == '.'; i--)res++;
for (int i = x; i < n && a[i][y] == '.'; i++) res++;
for (int i = y; i >= 0 && a[x][i] == '.'; i--) res++;
for (int i = y; i < m && a[x][i] == '.'; i++) res++;
cout << res - 3 << '\n';
return 0;
}
C 最小异或和
思路:为了尝试将 A A A 划分为一个或多个非空段落,我们可以尝试在相邻元素之间的 N − 1 N-1 N−1 个位置中选择放置“分隔符”的所有 2 ( N − 1 ) 2^{(N-1)} 2(N−1) 种组合方式。在实现上,一种有用的方法是进行位运算的穷举搜索,其中每个是否放置分隔符的 N − 1 N-1 N−1 个选择组合被视为 N − 1 N-1 N−1 位二进制整数,并对应于一个介于 0 0 0(含)和 2 ( N − 1 ) 2^{(N-1)} 2(N−1)(不含)之间的整数。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(0); cin.tie(0), cout.tie(0);
int n;
cin >> n;
vector<int>a(n);
for (int i = 0; i < n; i++)cin >> a[i];
int res = 2e9;
cout << (1 << 30) << '\n';
for (int i = 0; i < (1 << (n - 1)); i++) {
int tres = 0, t = 0;
for (int j = 0; j < n; j++) {
t |= a[j];
if ((1 << j)&i) {
tres ^= t;
t = 0;
}
}
tres ^= t;
res = min(res, tres);
}
cout << res << '\n';
return 0;
}
D 铺房间
思路:由于约束条件较小,其中
H
W
≤
16
HW \leq 16
HW≤16,因此我们可以考虑穷举地搜索覆盖的方式。
从左上角的单元格开始,进行深度优先搜索,尝试放置一个方形的榻榻米垫子,或者一个向左或向下伸出的矩形榻榻米垫子;这样,我们可以穷举地搜索所有的覆盖方式。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int h, w;
int f[20][20];
int dfs(int x, int y, int a, int b) {
if (x == h + 1 && y == 1)return 1;
int tx = x, ty = y + 1;
if (ty > w) {
tx++;
ty = 1;
}
if (f[x][y]) return dfs(tx, ty, a, b);
int res = 0;
if (b) {
f[x][y] = 1;
res += dfs(tx, ty, a, b - 1);
f[x][y] = 0;
}
if (a && y + 1 <= w && f[x][y + 1] == 0) {
f[x][y] = 1;
f[x][y + 1] = 1;
res += dfs(tx, ty, a - 1, b );
f[x][y] = 0;
f[x][y + 1] = 0;
}
if (a && x + 1 <= h && f[x + 1][y] == 0) {
f[x][y] = 1;
f[x + 1][y] = 1;
res += dfs(tx, ty, a - 1, b );
f[x][y] = 0;
f[x + 1][y] = 0;
}
return res;
}
int main() {
ios::sync_with_stdio(0); cin.tie(0), cout.tie(0);
int a, b;
cin >> h >> w >> a >> b;
cout << dfs(1, 1, a, b) << '\n';
return 0;
}
E 收集
思路:同一颜色的球应该以什么顺序收集。假设有 k k k个颜色为 i i i的球,它们的坐标为 x 1 , x 2 , x 3 , . . . , x k x_1, x_2, x_3, ..., x_k x1,x2,x3,...,xk(其中 x 1 < x 2 < x 3 < . . . < x k x_1 < x_2 < x_3 < ... < x_k x1<x2<x3<...<xk)。
在这里,可以证明无论前一个颜色的球收集后的位置如何,我们可以假设最后一个收集的球要么位于 x 1 x_1 x1,要么位于 x k x_k xk(在经过前一个颜色的球之后,不收集当前颜色的球是没有意义的,并且在经过 x 1 x_1 x1和 x k x_k xk之后,所有其他的球都已经经过)。
因此,我们可以使用以下动态规划(DP)算法解决该问题:
l d p [ i ] 和 r d p [ i ] ldp[i]和rdp[i] ldp[i]和rdp[i]表示完成收集所有颜色为 i i i的球所需的最少时间,其中 l d p ldp ldp表示位于该颜色为 i i i的球的最左边( x 1 x_1 x1), r d p rdp rdp表示位于最右边( x k x_k xk)。(我们假设不存在没有着色的颜色,即所有颜色都至少有一个球)
具体来说,设 l l l为颜色为 i i i的球的最小坐标, r r r为最大坐标,则在收集完颜色为 i − 1 i-1 i−1的球后,到达坐标 l l l所需的最少时间为:
- 如果 x ≤ r x \leq r x≤r:你先向右移动到 r r r,然后再向左移动到 l l l,所需时间为 ( r − x ) + ( r − l ) (r-x) + (r-l) (r−x)+(r−l)
- 如果 x > r x > r x>r:你只需要向左移动到 l l l,所需时间为 r − x r-x r−x
同样地,我们可以找到从收集了颜色为 i − 1 i-1 i−1后的坐标 x x x开始,到达坐标 r r r所需的最少时间。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(0); cin.tie(0), cout.tie(0);
int n;
cin >> n;
vector<int>e[n + 1];
for (int i = 0; i < n; i++) {
int x, c;
cin >> x >> c;
e[c].push_back(x);
}
vector<int>l(n + 1, 0), r(n + 1, 0);
vector<ll>ldp(n + 1, 0), rdp(n + 1, 0);
for (int i = 1; i <= n; i++) {
sort(e[i].begin(), e[i].end());
if (e[i].size()) {
ll tl = ldp[i - 1] + abs(e[i][e[i].size() - 1] - l[i - 1]) +
abs(e[i][0] - e[i][e[i].size() - 1]);
ll tr = rdp[i - 1] + abs(e[i][e[i].size() - 1] - r[i - 1]) +
abs(e[i][0] - e[i][e[i].size() - 1]);
ldp[i] = min(tl, tr);
tl = ldp[i - 1] + abs(e[i][0] - l[i - 1]) +
abs(e[i][e[i].size() - 1] - e[i][0]);
tr = rdp[i - 1] + abs(e[i][0] - r[i - 1]) +
abs(e[i][e[i].size() - 1] - e[i][0]);
rdp[i] = min(tl, tr);
l[i] = e[i][0];
r[i] = e[i][e[i].size() - 1];
}
else {
l[i] = l[i - 1];
r[i] = r[i - 1];
ldp[i] = ldp[i - 1];
rdp[i] = rdp[i - 1];
}
}
cout << min(ldp[n] + abs(l[n]), rdp[n] + abs(r[n]));
return 0;
}
F 最短回文
思路:假设在每个顶点1和N上都放有一块棋子,并且你可以重复以下操作:“将每个棋子移动到其相邻的顶点,使用具有相同字符的边”——直到两个棋子最终靠近。
对于回文串的长度为偶数的情况,只需要找到使两个棋子到达相同顶点的最小移动次数;对于长度为奇数的回文串,只需要找到使两个棋子之间的距离恰好为1的最小移动次数。
剩下的工作就是从对应于 ( 1 , N ) (1, N) (1,N)的顶点开始,在这个图上执行广度优先搜索(BFS),并检查偶数长度和奇数长度的回文串。(对于偶数长度的回文串,只需检查到达对应于 ( i , i ) (i, i) (i,i)的顶点的最短距离,对所有 i i i;对于奇数长度的回文串,只需检查对应于 ( a , b ) (a, b) (a,b)和 ( b , a ) (b, a) (b,a)的顶点之间的最短距离,对所有边 ( a , b ) (a, b) (a,b))。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(0); cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
vector<vector<vector<int>>>e(n, vector<vector<int>>(26));
vector<vector<int>>dis(n, vector<int>(n, -1));
queue<array<int, 2>>q;
for (int i = 0; i < n; i++) {
q.push({i, i});
dis[i][i] = 0;
}
for (int i = 0; i < m; i++) {
int u, v;
char c;
cin >> u >> v >> c;
int cc = c - 'a';
u--, v--;
if (u > v)swap(u, v);
if (dis[u][v] == -1) {
q.push({u, v});
dis[u][v] = 1;
}
e[u][cc].push_back(v);
e[v][cc].push_back(u);
}
while (q.size()) {
auto [x, y] = q.front();
q.pop();
for (int i = 0; i < 26; i++) {
for (auto j : e[x][i]) {
for (auto jj : e[y][i]) {
if (dis[min(j, jj)][max(j, jj)] == -1) {
dis[min(j, jj)][max(j, jj)] = dis[x][y] + 2;
q.push({min(j, jj), max(j, jj)});
}
}
}
}
}
cout << dis[0][n - 1];
return 0;
}