AtCoder Beginner Contest 224(A-F)
知识点整理:
题号 | 知识点 | 备注 |
---|---|---|
A | 无 | |
B | 无 | |
C | 图 | |
D | 基础DP | 矩阵最大路径和变种 |
E | 状态机DP | |
F | 状压DP | |
G | ? | |
H | ? |
A - QQ solver
题意
给你三个字符组成的乘法表达式, 求计算结果
题解
用scanf读入整数
#include <bits/stdc++.h>
using namespace std;
int main() {
int a, b;
scanf("%dx%d", &a, &b);
printf("%d\n", a*b);return 0;
}
B - Caesar Cipher
题意
给定两个字符串 A A A和 B B B, 问能否通过字母的轮换把A变成B
题解
一共就26种情况, 暴力枚举, 需注意char的最大范围是128, 有可能溢出.
#include <bits/stdc++.h>
using namespace std;
int main() {
string a, b;
cin >> a >> b;
bool flag = false;
for (int i = 0; i < 26; i++) {
string c = a;
for (int j = 0; j < c.size(); j++) {
c[j] = (char)(a[j] + i);
if (c[j] > 'z' || c[j] < 0) {
c[j] = (char) (a[j] - 26 + i);
}
}
if (c == b) {
flag = true;
break;
}
}
puts(flag ? "Yes" : "No");
return 0;
}
C - Graph Isomorphism
题意
两个图是否同构
题解
8个点, 一共就8!种情况, 暴力枚举
#include <bits/stdc++.h>
using namespace std;
const int N = 10;
int a[N][N];
int b[N][N];
struct node {
int x, y;
} c[N];
int n, m;
int vis[N];
int res[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
int main() {
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int x, y;
cin >> x >> y;
a[x][y] = 1;
a[y][x] = 1;
c[i] = {x, y};
}
for (int i = 1; i <= m; i++) {
int x, y;
cin >> x >> y;
b[x][y] = 1;
b[y][x] = 1;
}
bool flag = 0;
int cnt = 0;
int dex = 1;
for (int i = 1; i <= m; i++) {
if (b[res[c[i].x]][res[c[i].y]] == 0 &&
b[res[c[i].y]][res[c[i].x]] == 0) {
dex = 0;
break;
}
}
if(dex==1) {
puts("Yes");
return 0;
}
while (next_permutation(res + 1, res + n + 1)) {
int now = 1;
for (int i = 1; i <= m; i++) {
if (b[res[c[i].x]][res[c[i].y]] == 0 &&
b[res[c[i].y]][res[c[i].x]] == 0) {
now = 0;
break;
}
}
if (now == 1) {
flag = 1;
break;
}
}
if (flag)
puts("Yes");
else
puts("No");
}
D - Weak Takahashi
题意
给定一个矩阵 由 . . .和 # \# # 两种符号组成, 点代表豆子, #号代表一堵墙, 遇到墙就不能走了, 问从左上角出发, 至多吃到几个豆子?
题解
定义 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示走到 ( i , j ) (i, j) (i,j)点可以吃到多少豆子, 那么可以从上边或左边转移,取较大的情况,并维护下整个dp数组的最大值即可
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 110;
int h, w;
string c[MAXN];
int dp[MAXN][MAXN];
int main() {
cin >> h >> w;
for (int i = 1; i <= h ; i++) {
cin >> c[i];
c[i] = '$' + c[i];
}
if (c[1][1] == '.') dp[1][1] = 1;
int mx = dp[1][1];
for (int i = 1; i <= h; i++) {
for (int j = 1; j <= w; j++) {
if (i == 1 && j == 1) continue;
if (i > 1 && c[i-1][j] == '.') dp[i][j] = max(dp[i][j], dp[i-1][j]);
if (j > 1 && c[i][j-1] == '.') dp[i][j] = max(dp[i][j], dp[i][j-1]);
if (dp[i][j] && c[i][j] == '.') dp[i][j] ++;
mx = max(mx, dp[i][j]);
}
}
cout << mx<< endl;
}
E - Rook Path
题意
有一个KaTeX parse error: Undefined control sequence: \cross at position 2: H\̲c̲r̲o̲s̲s̲ ̲W的棋盘, 一个车从 ( x 1 , y 1 ) (x_1, y_1) (x1,y1)走 k k k步到 ( x 2 , y 2 ) (x_2, y_2) (x2,y2)有多少种方案?
题解
一开始的思路是dp, 定义 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]表示走 i i i步到 ( j , k ) (j, k) (j,k)点的方案数, 那么可以得到转移方程:
d
p
[
i
]
[
j
]
[
k
]
=
∑
1
≤
s
≤
W
&
&
s
!
=
j
d
p
[
i
−
1
]
[
s
]
[
k
]
+
∑
1
≤
s
≤
H
&
&
s
!
=
k
d
p
[
i
−
1
]
[
j
]
[
s
]
dp[i][j][k]=\sum_{1\le s \le W \&\& s!=j} dp[i-1][s][k] + \sum_{1\le s \le H \&\& s!=k} dp[i-1][j][s]
dp[i][j][k]=1≤s≤W&&s!=j∑dp[i−1][s][k]+1≤s≤H&&s!=k∑dp[i−1][j][s]
这样看来, 时间和空间复杂度都高达
O
(
H
W
K
)
O(HWK)
O(HWK), 显然不合要求, 考虑到大部分情况是一样的, 只跟是不是同行、同列有关, 因此把棋盘分成四种情况, 并分别用1 2 3 4表示:
1-跟终点同行 2-跟终点同列 3-等于终点 4-其他
那么就可以统计不同状态转移的方案数了:
d p [ i ] [ 1 ] = d p [ i − 1 ] [ 1 ] ∗ ( w − 2 ) + d p [ i − 1 ] [ 3 ] + d p [ i − 1 ] [ 4 ] ∗ ( w − 1 ) dp[i][1] = dp[i-1][1]*(w-2)+dp[i-1][3]+dp[i-1][4]*(w-1) dp[i][1]=dp[i−1][1]∗(w−2)+dp[i−1][3]+dp[i−1][4]∗(w−1)
d p [ i ] [ 2 ] = d p [ i − 1 ] [ 2 ] ∗ ( h − 2 ) + d p [ i − 1 ] [ 3 ] + d p [ i − 1 ] [ 4 ] ∗ ( h − 1 ) dp[i][2] = dp[i-1][2]*(h-2)+dp[i-1][3]+dp[i-1][4]*(h-1) dp[i][2]=dp[i−1][2]∗(h−2)+dp[i−1][3]+dp[i−1][4]∗(h−1)
d p [ i ] [ 3 ] = d p [ i − 1 ] [ 1 ] + d p [ i − 1 ] [ 2 ] dp[i][3] = dp[i-1][1]+dp[i-1][2] dp[i][3]=dp[i−1][1]+dp[i−1][2]
d p [ i ] [ 4 ] = d p [ i − 1 ] [ 1 ] ∗ ( h − 1 ) + d p [ i − 1 ] [ 2 ] ∗ ( w − 1 ) + d p [ i − 1 ] [ 3 ] ∗ ( h + w − 4 ) dp[i][4] = dp[i-1][1]*(h-1)+dp[i-1][2]*(w-1)+dp[i-1][3]*(h+w-4) dp[i][4]=dp[i−1][1]∗(h−1)+dp[i−1][2]∗(w−1)+dp[i−1][3]∗(h+w−4)
以第一行为例解释下:
状态1 可以通过状态1, 3, 4转移来, 固定状态1点集中的一个点:
- 状态1转移到状态1: 只要不是相同点就能转移来, 有 w − 2 w-2 w−2种情况
- 状态3转移到状态1: 只能走一种情况
- 状态4转移到状态1: 有 w − 1 w-1 w−1种情况可以转移来
其他转移关系同理., 最后答案是 d p [ k ] [ 3 ] dp[k][3] dp[k][3]
#include <bits/stdc++.h>
typedef long long LL;
#define int long long
using namespace std;
const int MAXL = 1 << 22;
char i_str[MAXL], o_str[MAXL], *i_s, *i_t;
int o_t;
#define Flush fwrite(o_str,1,o_t,stdout), o_t = 0
#define space pc(' ')
#define enter pc('\n')
inline char gc() {
if (i_s == i_t) {
i_s = i_str;
i_t = i_s + fread(i_str, 1, MAXL, stdin);
return i_s == i_t ? EOF : *i_s++;
} else
return *i_s++;
}
inline int read() {
int x = 0, f = 0;
char ch = gc();
for (; ch < '0' || ch > '9'; ch = gc())
if (ch == '-')
f = 1;
for (; ch >= '0' && ch <= '9'; ch = gc())
x = x * 10 + (ch ^ 48);
return f ? -x : x;
}
inline void pc(char x) {
o_str[o_t++] = x;
if (o_t == MAXL)
Flush;
}
void write(int x) {
if (x < 0)
pc('-'), x = -x;
if (x > 9)
write(x / 10);
pc(x % 10 ^ 48);
}
const int N = 1e6 + 6, Mod = 998244353;
int n, m, k, a, b, c, d, f[N][4];
signed main() {
n = read(), m = read(), k = read();
a = read(), b = read(), c = read(), d = read();
if (a != c && b != d)
f[0][0] = 1;
if (a == c && b != d)
f[0][1] = 1;
if (a != c && b == d)
f[0][2] = 1;
if (a == c && b == d)
f[0][3] = 1;
for (register int i = 1; i <= k; ++i) {
f[i][0] = ((f[i - 1][1] * (n - 1) % Mod + f[i - 1][2] * (m - 1)) % Mod + f[i - 1][0] * (n + m - 4) % Mod) % Mod;
f[i][1] = ((f[i - 1][0] + f[i - 1][3] * (m - 1) % Mod) % Mod + f[i - 1][1] * (m - 2) % Mod) % Mod;
f[i][2] = ((f[i - 1][0] + f[i - 1][3] * (n - 1) % Mod) % Mod + f[i - 1][2] * (n - 2) % Mod) % Mod;
f[i][3] = (f[i - 1][1] + f[i - 1][2]) % Mod;
}
write(f[k][3]), enter ;
Flush ;
return 0;
}
F - Simple Operations on Sequence
题意
给你两个长度为 N N N的序列 A , B A,B A,B, 有两种操作:
- 花 X X X日元把 A i A_i Ai加1或减1
- 花 Y Y Y日元交换 A i , A i + 1 A_i, A_{i+1} Ai,Ai+1
问至少花多少钱把 A A A变成 B B B?
题解
N ≤ 18 N\le 18 N≤18, 所以可以考虑 状态压缩, 那么难点就在如何定义状态
本题的状态定义为:
把 A A A中的某 i i i个数变成 B B B的前 i i i个数的最小花销, 这个其实挺难想到的, 通常会想的是变成对应的.
那么 A A A中哪i个数, 就看状态值中1的位置在哪了, 然后看转移关系.
假设当前状态是
d
p
[
S
]
dp[S]
dp[S], 其中
S
S
S中有i个1, (这个值在C++中有一个函数__builtin_popcount
可以快速计算出来), 那么我们需要把某个数换到
B
i
+
1
B_{i+1}
Bi+1上, 这个数在
S
S
S中对应的状态一定是0, 我们枚举这个为0的位置
j
j
j, 进行两步操作, 先把
A
j
A_j
Aj变成
B
i
+
1
B_{i+1}
Bi+1, 再换到
i
+
1
i+1
i+1位上, 计算花多少钱, 维护最小值.
那么接下来要解决的是**把 A j A_j Aj变成 B i + 1 B_{i+1} Bi+1 **要操作多少次, 这个次数等价于j右面有多少个0, 因为前面为1的位置已经确定了, 会把未确定的数“赶过来”, 所以剩下几个未确定的的数, 就是交换几次.
#include <bits/stdc++.h>
using namespace std;
const int N = 1 << 18;
#define ll long long
const ll inf = 2e18;
int n;
ll a[30], b[30];
ll f[N], x, y;
bool c[30];
ll F(int state, int now) {
int res = 0;
for (int i = 1; i <= n; i++) {
if ((state & (1 << (i - 1))) == 0 && i < now) res++;
}
return res;
}
int main() {
scanf("%d%lld%lld", &n, &x, &y);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
}
for (int i = 1; i <= n; i++) {
scanf("%lld", &b[i]);
}
f[0] = 0;
for (int i = 1; i < (1 << n); i++) {
f[i] = inf;
}
for (int state = 0; state < (1 << n); state++) {
int cntS = 0;
for (int j = 1; j <= n; j++) {
if (state & (1 << (j - 1))) {
cntS++;
}
}
for (int j = 1; j <= n; j++) {
if (state & (1 << (j - 1))) continue;
f[state | (1 << (j - 1))] = min(f[state | (1 << (j - 1))], f[state] + abs(a[cntS + 1] - b[j]) * x + F(state, j) * y);
}
}
printf("%lld\n", f[(1 << n) - 1]);
}