AtCoder Beginner Contest 212

本文详细解析了AtCoder Beginner Contest 212的七道编程题目,包括题目大意、解题思路和算法实现。涵盖题目包括合金问题、弱密码判断、最小差值计算、集合查询、安全旅程、贪婪的Takahashi和强力对等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A - Alloy

如题

#include <cstdio>

using namespace std;

int main() {
    int a, b;
    scanf("%d%d", &a, &b);
    printf("%s\n", a ? b ? "Alloy" : "Gold" : "Silver");
    return 0;
}

B - Weak Password

题目大意:

四位数密码满足以下任一条件被判定为Weak,否则为Strong

  1. 四位数均相同;
  2. 从千位至个位依次递增,如01231234等,特别地,901289017890也被是为Weak

大致思路:

  1. 用一个整型变量读入单个四位数

我们会发现,当这个四位数是1111的倍数时,即a % 1111 == 0时,满足条件一;而对于条件二,除了那三种特殊情况外,均满足a % 1111 == 123 && a < 7000,即解。

#include <cstdio>

using namespace std;

int main() {
    int a;
    scanf("%d", &a);
    if(!(a % 1111) || (a % 1111 == 123 && a < 7000) || a == 7890 || a == 8901 || a == 9012)
        printf("Weak\n");
    else printf("Strong\n");
    return 0;
}
  1. 拆分成四个一位数理解

当四个变量分别相等时,满足条件一;条件二(前一个变量加一等于后一个变量)可以通过对10取余实现。

#include <cstdio>

using namespace std;

int main()
{
	int a, b, c, d;
	scanf("%1d%1d%1d%1d", &a, &b, &c, &d);
	if(a == b && b == c && c == d) printf("Weak\n"); //条件一
	else if(b == (a + 1) % 10 && c == (b + 1) % 10 && d == (c + 1) % 10) printf("Weak\n"); //条件二
	else printf("Strong\n");
	return 0;
}
  1. 按照字符理解

当四个字符分别相等时,满足条件一;条件二理解为b-a==1 || a-b==9

#include <cstdio>
#include <cstring>

using namespace std;

int main() {
    char s[4];
    scanf("%s", s);
    bool same = true, step = true;
	for(int i = 0; i < 3; i++) {
		if (s[i] != s[i + 1]) same = false;
		if (((s[i] + 1) % 10) != (s[i + 1] % 10)) step = false;
	}
	if(same || step) printf("Weak\n");
	else printf("Strong\n");
    return 0;
}

C - Min Difference

题目大意:

给定两个数组A、B,求|A[i] - B[j]|的最小值。

大致思路:

首先两两比较的复杂度是O(MN),一定会超时,因此要想办法优化一下。

差值会在A[i] > B[j]A[i] < B[j]的交界处产生(被减数越小,减数数越大,差值越小)。这里我们用增大减数的方法求解:将A、B中的元素分别从小到大排列,利用两个从1划到n的指针p、q,当A[p] > B[q]时,q向后移动;反之,移动p。

#include <cstdio>
#include <cmath>
#include <algorithm>

using namespace std;

int a[200010], b[200010];

int main() {
	int n, m;
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    sort(a + 1, a + n + 1);
	for(int i = 1; i <= m; i++)
        scanf("%d", &b[i]);
    sort(b + 1, b + m + 1);
	int p = 1, q = 1, ans = abs(a[1] - b[1]);
	while(p <= n && q <= m) {
		ans = min(ans, abs(a[p] - b[q]));
		if(a[p] < b[q]) p++;
		else q++;
	}
	printf("%d\n", ans);
	return 0;
}

D - Querying Multiset

题目大意:

初始时容器为空,执行n次操作,每种操作都属于以下三种类型之一:

  1. 将一个新的整数x添加到容器中;
  2. 现在容器中的每个数都加x;
  3. 把容器中最小的数取出来,记录并丢掉(有多个最小数时,只取一个就行)。

要求按照询问顺序输出类型三操作中所记录的数。

大致思路:

这里需要一个能够实现插入、删除元素,有优先级又不自动去重的容器,可以联想到优先队列(priority_queue)multiset

  1. 优先队列虽然能够自定义优先级,但是只能访问到(具有最高优先级的)堆顶元素,不能直接实现类型二中修改数值的功能。因此我们可以利用类似于线段树中的lazy标记(把要加的数都记在小本本上,秋后算账)来实现这一操作。
#include <cstdio>
#include <queue>
#define ll long long

using namespace std;

int main() {
    ll n;
    scanf("%lld", &n);
    priority_queue<ll, vector<ll>, greater<ll> > a;
    ll Lazy = 0;
    for(ll i = 0; i < n; i++) {
        int p;
        scanf("%d", &p);
        if(p == 3) {
            printf("%lld\n", Lazy + a.top());
            a.pop();
        }
        else {
            ll q;
            scanf("%lld", &q);
            if(p == 1) a.push(q - Lazy);
            else Lazy += q;
        }
    }
    return 0;
}
  1. 至于multiset,它是<set>库中一个非常有用的类型,可以看成一个序列,能够在O(logn)的时间内完成插入(或删除)一个数,而且他能够时刻保证序列有序,允许存在重复的数(multiset与set最大的区别也在于是否拥有去重功能)。
#include <cstdio>
#include <set>
#define ll long long

using namespace std;

int main() {
    ll n;
    scanf("%lld", &n);
    multiset<ll> st;
    ll Lazy = 0;
    for(ll i = 0; i < n; i++) {
        int p;
        scanf("%d", &p);
        if(p == 3) {
            multiset<ll>::iterator it = st.begin();
			printf("%lld\n", (*it) + Lazy);
			st.erase(it);
        }
        else {
            ll q;
            scanf("%lld", &q);
            if(p == 1) st.insert(q - Lazy);
            else Lazy += q;
        }
    }
    return 0;
}

E - Safety Journey

题目大意:

有N个原本两两之间直接连通的城市,随着时间的推移,有M条路无法使用,其中第i条路连接着城市U[i]和城市V[i]。(有一个全联通的双向图,删掉它的M条边,其中第i条边的端点为U[i]V[i]。)

现在要进行K天的旅行,起点与终点均为城市1,且要求任意相邻的两天都不在同一个城市。(求一个长度为K+1的序列A(A[0], A[1], ... , A[K]),要求A[0] = A[K] = 1且对于对于任意节点满足A[i] != A[i + 1]。)

输出符合要求的不同的旅行方案数,结果对998244353取模。对于方案P、Q而言,只要有一个城市不同,就认为这两种方案是不同的。

大致思路:

我们尝试用动态规划解决本题。

dp[i][j]表示城镇序列数量(A[0], ... , A[i])使得A[0] = 0A[i] = j,并且对于任意0 <= i' <= i-1都满足A[i']A[i'+1]彼此相连。然后dp[0][0] = 1dp[0][1] = ... = dp[0][N] = 0dp[i+1][j]就可以表达为

d p [ i + 1 ] [ j ] = ∑ j ′ ∈ S j d p [ i ] [ j ′ ] dp[i+1][j]=\displaystyle\sum_{j'\in S_j}dp[i][j'] dp[i+1][j]=jSjdp[i][j]
其中S(j)jj'间有道路相连的集合。

然而,直接计算需要O(N^2K)的复杂度,这不太行,所以,我们将表达式转换为
d p [ i + 1 ] [ j ] = ∑ j ′ = 1 N d p [ i ] [ j ′ ] − ∑ j ′ ∈ S j d p [ i ] [ j ′ ] dp[i+1][j]=\displaystyle\sum_{j'=1}^Ndp[i][j']-\displaystyle\sum_{j'\in S_j}dp[i][j'] dp[i+1][j]=j=1Ndp[i][j

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值