「Atcoder」abc241 题解

A - Digit Machine

Code
#include <bits/stdc++.h>
using namespace std;
 
int n, x, a[10];
 
int main()
{
    while (cin >> x)    a[n++] = x;    
 
    int t = 0;
    for (int i = 1; i <= 3; i++)
    {
        t = a[t];
    }
 
    cout << t;
    return 0;
}

B - Pasta

Code
#include <bits/stdc++.h>
using namespace std;
 
int n, m;
unordered_map<int, int> cnt;
 
int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		int x;
		cin >> x;
		cnt[x]++;
	}	
	
	while (m--)
	{
		int x;
		cin >> x;
		if (cnt[x] > 0)	cnt[x]--;
		else 
		{
			puts("No");
			return 0;
		}
	}
	puts("Yes");
}

C - Connect 6

解题思路

1.枚举每个格子构成的6 * 6 网格(注意处理边界问题)
2.只有当横、竖或者两条对角线的黑色方格数>= 4时,才可以成功(因为最多涂黑两个方格)

方格图对角线元素点击此处查看,加深理解
TIPS:下面代码枚举反对角线时是构某个格子的右上方6 * 6网格
OS:很难想象学了这么久我竟然都不会枚举正、反对角线元素,我是rz

Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;

int n;
string g[N];

int main()
{
    cin >> n;
    for (int i = 0; i < n; i++) cin >> g[i];

    bool ans = false;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
        {
            if (i + 5 < n) 
            {
                int cnt = 0;
                for (int k = 0; k < 6; k++) 
                    if (g[i + k][j] == '#') cnt++;
                if (cnt >= 4)   ans = true;
            }
            if (j + 5 < n)
            {
                int cnt = 0;
                for (int k = 0; k < 6; k++)
                    if (g[i][j + k] == '#') cnt++;
                if (cnt >= 4)   ans = true;
            }
            if (i + 5 < n && j + 5 < n)
            {
                int cnt = 0;
                for (int k = 0; k < 6; k++)
                    if (g[i + k][j + k] == '#') cnt++;
                if (cnt >= 4)   ans = true;
            }
            if (i - 5 >= 0 && j + 5 < n)
            {
                int cnt = 0;
                for (int k = 0; k < 6; k++)
                    if (g[i - k][j + k] == '#') cnt++;
                if (cnt >= 4)   ans = true;
            }
        }

    if (ans)    puts("Yes");
    else puts("No");
    return 0;
}

D - Sequence Query(Multiset + 二分)

题目描述

我们有一个空序列。
给定Q个查询,按顺序处理它们。
每个查询属于以下三种类型之一。
1 x:将 x插入到 A
2 x k:找到小于等于x的集合中第k大的数
3 x k:找到大于等于x的集合中第k小的数
数据范围:
1 ≤ Q ≤ 2 ∗ 1 0 5 1 \le Q \le 2 * 10 ^5 1Q2105
1 ≤ x ≤ 1 0 1 8 1 \le x \le 10 ^18 1x1018
1 ≤ k ≤ 5 1 \le k \le 5 1k5

解题思路

本题用到了multiset数据结构,可以做到让元素从小到大排序,并且可以重复
然后再结合multiset自带的二分查找( O ( l o g n ) O(logn) O(logn))给定的元素
分情况:
1. 对于op == 2的操作, 使用upper_bound找大于元素x的第一个位置,然后往前找,次数为k
2. 对于op == 2的操作, 使用lower_bound找大于等于元素的第一个位置,然后往后找,由于这个位置已经符合条件,所以次数为k - 1

OS: 比赛的时候没有正确分析复杂度,思路很接近正解,但是没有用二分。 被很多题解被迷惑了,导致补题的时候被卡了两个小时!

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
bool flag;
ll q, op, x, k;
multiset<long long> s;
 
int main()
{
	cin >> q;
	while (q--)
    {
		cin >> op >> x;
		if (op == 1) s.insert(x);
		else if (op == 2)
        {
			cin >> k;
			auto it = s.upper_bound(x);
			flag = true;
			
            for (int i = 1; i <= k; i++)  // 每次都移动it指针
            {
				if (it == s.begin()) flag = false;  // 只有当当前元素不是begin(), 才说明前面还有元素
				else --it;  // 前面还有元素,所以--
			}
			if (flag)    cout << *it << endl;  
			else puts("-1");
		}
		else
        {
			cin >> k;
			auto it = s.lower_bound(x);
			flag = true;
 
			for (int i = 1; i < k; i++)   // 由于找到的这个位置已经符合条件,所以次数为 k - 1
            {
				if (it == s.end())   flag = false;  // 如果到了末尾,就置为false
				else ++it;  // 只有不是末尾时,才++; 在 i == k时,有可能这一次移动导致移到了end,所以有了下面的特判
			}
			if (it == s.end())   flag = false;  // 特判最后一个元素, 因为可能最后一次移动到了end
			if (flag)   cout << *it << endl;
			else puts("-1");
		}
	}
	return 0;
}

E - Putting Candies

题目描述

avatar

解题思路

OS:以后再补吧,没时间咯
详细题解参考此处

F - Skate(BFS)

题目描述

avatar

解题思路

经观察可得,只有障碍物的四周能达到
所以我们只需要分别对行、列枚举障碍物的四周
用二分查找到同行同列的元素,并进行判断
如果每次能找到符合条件的元素的话,就在原先的基础上距离 + 1
详细题解参考此处

Code
#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;
const int N = 100010;
typedef pair<int, int> PII;

int h, w, n, sx, sy, ex, ey;
map<PII,int> vis;
PII row[N], col[N];
struct node
{
	int x, y, d;
};

int BFS()
{
	queue<node> q;
	q.push({sx, sy, 0});
	
	while (q.size())
	{
		auto t = q.front();
		q.pop();
        if (vis[{t.x, t.y}]) continue;
        vis[{t.x, t.y}] = 1;
		
		if (t.x == ex && t.y == ey) return t.d;
		
		auto it = lower_bound(row + 1, row + n + 1, PII {t.x, t.y});  // 在同行中变换列
		int tx = it->x, ty = it->y;
		if (t.x == tx && t.y != ty - 1) q.push({tx, ty - 1, t.d + 1});

		it--;
	    tx = it->x, ty = it->y;
	    if (t.x == tx && t.y != ty + 1)	q.push({tx, ty + 1, t.d + 1});   	

		it = lower_bound(col + 1, col + n + 1, PII {t.y, t.x});  // 在同列中变换行
		ty = it->x, tx = it->y;
		if (t.y == ty && t.x != tx - 1)	q.push({tx - 1, ty, t.d + 1});	
		
		it--;   
		ty = it->x, tx = it->y;
		if (t.y == ty && t.x != tx + 1)	q.push({tx + 1, ty, t.d + 1});	
	}
	return -1;	
}

int main()
{
	cin >> h >> w >> n;
	cin >> sx >> sy >> ex >> ey;
	for (int i = 1; i <= n; i++)  // 从1读可以保证row,col中有{0,0}以免--it出现边界问题
	{
		int r, c;
		cin >> r >> c;
		row[i] = {r, c};
		col[i] = {c, r};
	}
	sort(row + 1, row + 1 + n); sort(col + 1, col + 1 + n);
	
	cout << BFS() << endl;
	return 0;
}

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
AtCoder Beginner Contest 134 是一场 AtCoder 的入门级比赛,以下是每道题的简要题解: A - Dodecagon 题目描述:已知一个正十二边形的边长,求它的面积。 解题思路:正十二边形的内角为 $150^\circ$,因此可以将正十二边形拆分为 12 个等腰三角形,通过三角形面积公式计算面积即可。 B - Golden Apple 题目描述:有 $N$ 个苹果和 $D$ 个盘子,每个盘子最多可以装下 $2D+1$ 个苹果,求最少需要多少个盘子才能装下所有的苹果。 解题思路:每个盘子最多可以装下 $2D+1$ 个苹果,因此可以将苹果平均分配到每个盘子中,可以得到最少需要 $\lceil \frac{N}{2D+1} \rceil$ 个盘子。 C - Exception Handling 题目描述:给定一个长度为 $N$ 的整数序列 $a$,求除了第 $i$ 个数以外的最大值。 解题思路:可以使用两个变量 $m_1$ 和 $m_2$ 分别记录最大值和次大值。遍历整个序列,当当前数不是第 $i$ 个数时,更新最大值和次大值。因此,最后的结果应该是 $m_1$ 或 $m_2$ 中较小的一个。 D - Preparing Boxes 题目描述:有 $N$ 个盒子和 $M$ 个物品,第 $i$ 个盒子可以放入 $a_i$ 个物品,每个物品只能放在一个盒子中。现在需要将所有的物品放入盒子中,每次操作可以将一个盒子内的物品全部取出并分配到其他盒子中,求最少需要多少次操作才能完成任务。 解题思路:首先可以计算出所有盒子中物品的总数 $S$,然后判断是否存在一个盒子的物品数量大于 $\lceil \frac{S}{2} \rceil$,如果存在,则无法完成任务。否则,可以用贪心的思想,每次从物品数量最多的盒子中取出一个物品,放入物品数量最少的盒子中。因为每次操作都会使得物品数量最多的盒子的物品数量减少,而物品数量最少的盒子的物品数量不变或增加,因此这种贪心策略可以保证最少需要的操作次数最小。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wiz1code(算法号)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值