A - Full House 2
思路
- 统计每个数字出现的字数,再统计次数的值,必须满足两个次数值都为 2 2 2 或者一个为 3 3 3 一个为 1 1 1。
过题代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 100;
int x;
int cnt[maxn], ccnt[maxn];
bool judge() {
for (int i = 1; i <= 13; ++i) {
++ccnt[cnt[i]];
}
return ccnt[2] == 2 || (ccnt[3] == 1 && ccnt[1] == 1);
}
int main() {
ios::sync_with_stdio(false);
for (int i = 0; i < 4; ++i) {
cin >> x;
++cnt[x];
}
cout << (judge() ? "Yes" : "No") << endl;
return 0;
}
B - Calculator
思路
- 如果有
2
2
2 个
0
0
0 就一次按下
00
,否则都直接按一个数字。
过题代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1000 + 100;
int ans;
char str[maxn];
int main() {
ios::sync_with_stdio(false);
cin >> str;
for (int i = 0; str[i] != '\0'; ++i) {
if (str[i] == '0' && str[i + 1] == '0') {
++i;
}
++ans;
}
cout << ans << endl;
return 0;
}
C - Operate 1
题解
- 见 F - Operate K 题解
D - Diagonal Separation
思路
- 将所有输入以行为第一关键字、列为第二关键字排序,依次判断这些字符是否合法;
- 用 l a s t I d x lastIdx lastIdx 标记黑色方格可能的最大的列数,在处理的过程中,若碰到位于 ( X i , Y i ) (X_i,Y_i) (Xi,Yi) 的白色方格,黑色方格最大只能在这个白色方格的左边一位,更新 l a s t I d x = min ( l a s t I d x , Y i − 1 ) lastIdx=\min(lastIdx,Y_i-1) lastIdx=min(lastIdx,Yi−1);
- 若碰到位于
(
X
i
,
Y
i
)
(X_i,Y_i)
(Xi,Yi) 的黑色方格,必须满足
Y
i
≤
l
a
s
t
I
d
x
Y_i\leq lastIdx
Yi≤lastIdx,不满足则输出
No
; - l a s t I d x lastIdx lastIdx 初始值为 n n n。
过题代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 200000 + 100;
struct Node {
int x, y;
char ch;
};
bool operator<(const Node& a, const Node& b) {
if (a.x == b.x) {
return a.y < b.y;
}
return a.x < b.x;
}
int n, m;
Node node[maxn];
bool judge() {
int lastIdx = n;
sort(node, node + m);
for (int i = 0; i < m; ++i) {
if (node[i].ch == 'W') {
lastIdx = min(lastIdx, node[i].y - 1);
continue;
}
if (node[i].y > lastIdx) {
return false;
}
}
return true;
}
int main() {
ios::sync_with_stdio(false);
cin >> n >> m;
for (int i = 0; i < m; ++i) {
cin >> node[i].x >> node[i].y >> node[i].ch;
}
cout << (judge() ? "Yes" : "No") << endl;
return 0;
}
E - Maximize XOR
思路
- 用 dfs 暴搜会超时,暴搜复杂度是 O ( k × C n k ) O(k\times C_n^k) O(k×Cnk),当 k k k 比较大时计算次数可能达到 2 × 1 0 5 × 1 0 6 2\times10^5\times10^6 2×105×106;
- 当 k k k 比较大时,可以将 C n k C_n^k Cnk 转化为 C n n − k C_n^{n-k} Cnn−k,即从枚举需要选择哪些点改为枚举不需要选择哪些点;
- 由于数据保证 C n k ≤ 1 0 6 C_n^k\leq10^6 Cnk≤106,可以用以下暴力判定代码得到 min ( n − k , k ) ≤ 11 \min(n-k,k)\leq11 min(n−k,k)≤11,因此时间复杂度 O ( min ( k , n − k ) × C n k ) O(\min(k,n-k)\times C_n^k) O(min(k,n−k)×Cnk) 最坏情况下不超过 11 × 1 0 6 11\times10^6 11×106。
暴力代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 200000 + 100;
bool judgeC(LL n, LL k) {
LL ans = 1;
for (LL i = 1; i <= k; ++i) {
ans = ans * (n - i + 1) / i;
if (ans > 1000000) {
return false;
}
}
return true;
}
int main() {
ios::sync_with_stdio(false);
int maxK = 0;
for (int n = 1; n <= 200000; ++n) {
for (int k = 1; k <= n; ++k) {
if (judgeC(n, k)) {
maxK = max(maxK, min(k, n - k));
} else {
break;
}
}
}
cout << maxK << endl;
return 0;
}
过题代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 200000 + 100;
int n, k;
LL ans, tmp;
LL num[maxn];
void dfs(int depth, LL sum, int k) {
if (k == 0) {
ans = max(ans, sum);
return;
}
if (depth == n) {
return;
}
if (k != 0) {
dfs(depth + 1, sum ^ num[depth], k - 1);
}
if (k <= n - depth) {
dfs(depth + 1, sum, k);
}
}
int main() {
ios::sync_with_stdio(false);
cin >> n >> k;
for (int i = 0; i < n; ++i) {
cin >> num[i];
}
if (k > n / 2) {
k = n - k;
for (int i = 0; i < n; ++i) {
tmp ^= num[i];
}
}
dfs(0, tmp, k);
cout << ans << endl;
return 0;
}
F - Operate K
思路
- 在不考虑 S S S 与 T T T 字符串长度的情况下,求最短编辑距离是一道经典的动态规划问题,定义 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示 S S S 串的前 i i i 个字符构成的子串到 T T T 串的前 j j j 个字符构成的子串的最短编辑距离,递推公式为:
d p [ i ] [ j ] = min ( d p [ i − 1 ] [ j ] + 1 , d p [ i ] [ j − 1 ] + 1 , d p [ i − 1 ] [ j − 1 ] + ( S [ i ] ≠ T [ j ] ) ) dp[i][j]=\min(dp[i−1][j]+1,dp[i][j−1]+1,dp[i−1][j−1]+(S[i]\neq T[j])) dp[i][j]=min(dp[i−1][j]+1,dp[i][j−1]+1,dp[i−1][j−1]+(S[i]=T[j]))
- 答案为 d p [ ∣ S ∣ ] [ ∣ T ∣ ] ≤ K dp[|S|][|T|]\leq K dp[∣S∣][∣T∣]≤K,时间复杂度 O ( ∣ S ∣ ∣ T ∣ ) O(|S||T|) O(∣S∣∣T∣);
- 本题
S
S
S 串与
T
T
T 串长度均为
5
×
1
0
5
5\times10^5
5×105,但
K
K
K 最大只有
20
20
20,所以
d
p
dp
dp 中有很大一部分(
a
b
s
(
i
−
j
)
>
K
abs(i-j)>K
abs(i−j)>K 的部分)是无效的,可以直接输出
No
; - 只需要考虑 a b s ( i − j ) ≤ K abs(i-j)\leq K abs(i−j)≤K 的部分,重新定义 d p [ i ] [ k ] dp[i][k] dp[i][k] 表示字符串 S S S 前 i i i 个字符组成的子串与字符串 T T T 前 j = i + k j=i+k j=i+k 个字符组成的子串的最短编辑距离,其中 k ∈ [ − K , K ] k\in[-K,K] k∈[−K,K],表示相对于位置 i i i 的偏移,同样可以用以上递推公式计算结果,只是需要做一定改变:
d p [ i ] [ k ] = min ( d p [ i − 1 ] [ k + 1 ] + 1 , d p [ i ] [ k − 1 ] + 1 , d p [ i − 1 ] [ k ] + ( S [ i ] ≠ T [ i + k ] ) ) dp[i][k]=\min(dp[i−1][k+1]+1,dp[i][k−1]+1,dp[i−1][k]+(S[i]\neq T[i+k])) dp[i][k]=min(dp[i−1][k+1]+1,dp[i][k−1]+1,dp[i−1][k]+(S[i]=T[i+k]))
- 初始值为 d p [ 0 ] [ 0 ] = 0 dp[0][0]=0 dp[0][0]=0,其余值 d p [ i ] [ k ] = ∞ dp[i][k]=\infty dp[i][k]=∞,答案就是 d p [ ∣ S ∣ ] [ ∣ T ∣ − ∣ S ∣ ] ≤ K dp[|S|][|T|-|S|]\leq K dp[∣S∣][∣T∣−∣S∣]≤K;
- 最后,用一个偏移量将 k k k 的取值范围从 [ − K , K ] [-K,K] [−K,K] 移动至 [ 0 , 2 K ] [0,2K] [0,2K],只做有效位转移的情况下,时间复杂度与空间复杂度均为 O ( 2 K ∣ S ∣ ) O(2K|S|) O(2K∣S∣)。
过题代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 500000 + 100;
int k, slen, tlen;
char s[maxn], t[maxn];
int dp[maxn][100];
int main() {
ios::sync_with_stdio(false);
cin >> k >> (s + 1) >> (t + 1);
slen = strlen(s + 1);
tlen = strlen(t + 1);
if (abs(slen - tlen) > k) {
cout << "No" << endl;
return 0;
}
memset(dp, 0x3f, sizeof(dp));
dp[0][k] = 0;
for (int i = 0; i <= slen; ++i) {
for (int kk = -k; kk <= k; ++kk) {
int j = i + kk;
if (j < 0 || j > tlen) {
continue;
}
int kkk = kk + k;
if (i > 0 && kkk + 1 <= 2 * k) {
dp[i][kkk] = min(dp[i][kkk], dp[i - 1][kkk + 1] + 1);
}
if (j > 0 && kkk - 1 >= 0) {
dp[i][kkk] = min(dp[i][kkk], dp[i][kkk - 1] + 1);
}
if (i > 0 && j > 0) {
dp[i][kkk] = min(dp[i][kkk], dp[i - 1][kkk] + (s[i] != t[j]));
}
}
}
cout << (dp[slen][k + tlen - slen] <= k ? "Yes" : "No") << endl;
return 0;
}