AtCoder Beginner Contest 386 (ABC 386) A-F 题解

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,Yi1)
  • 若碰到位于 ( X i , Y i ) (X_i,Y_i) (Xi,Yi) 的黑色方格,必须满足 Y i ≤ l a s t I d x Y_i\leq lastIdx YilastIdx,不满足则输出 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} Cnnk,即从枚举需要选择哪些点改为枚举不需要选择哪些点;
  • 由于数据保证 C n k ≤ 1 0 6 C_n^k\leq10^6 Cnk106,可以用以下暴力判定代码得到 min ⁡ ( n − k , k ) ≤ 11 \min(n-k,k)\leq11 min(nk,k)11,因此时间复杂度 O ( min ⁡ ( k , n − k ) × C n k ) O(\min(k,n-k)\times C_n^k) O(min(k,nk)×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[i1][j]+1,dp[i][j1]+1,dp[i1][j1]+(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(ij)>K 的部分)是无效的,可以直接输出 No
  • 只需要考虑 a b s ( i − j ) ≤ K abs(i-j)\leq K abs(ij)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[i1][k+1]+1,dp[i][k1]+1,dp[i1][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][TS]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(2KS)

过题代码

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值