[补题记录] AtCoder Beginner Contest 301(A、B、C、D、E)

URL:https://atcoder.jp/contests/abc301

目录

A

Problem/题意

Thought/思路

Code/代码

B

Problem/题意

Thought/思路

Code/代码

C

Problem/题意

Thought/思路

Code/代码

D

Problem/题意

Thought/思路

Code/代码

E

Problem/题意

Thought/思路

Code/代码


A

Problem/题意

给一个只含 A 或 T 的字符串,如果 A 多,输出 A;如果 T 多,输出 T。如果一样多,从左到右数,谁先数完输出谁。

Thought/思路

好难,没有思路。

Code/代码

#include "bits/stdc++.h"

signed main() {
	int n; std::cin >> n;
	std::string s; std::cin >> s;

	int a = 0, t = 0;
	for (int i = 0; i < s.length(); ++ i) {
		if (s[i] == 'T') t ++;
		if (s[i] == 'A') a ++;
	}

	if (t == a) {
		int ra = 0, rt = 0;
		for (int i = 0; i < s.length(); ++ i) {
			if (s[i] == 'T') rt ++;
			if (s[i] == 'A') ra ++;
			if (rt == t) {
				std::cout << "T\n";
				break;
			}
			if (ra == a) {
				std::cout << "A\n";
				break;
			}
		}
	} else {
		if (t > a) std::cout << "T\n";
		else std::cout << "A\n";
	}
	
}

B

Problem/题意

给一个序列a,进行下面两种操作:

1.若 a[i] > a[i + 1],在 i 和 i + 1 之间插入 a[i] 和 a[i + 1] 之间所有的数,满足递减;

2.若 a[i] < a[i + 1],在 i 和 i + 1 之间插入 a[i] 和 a[i + 1] 之间所有的数,满足递增;

Thought/思路

写在题意里了。

Code/代码

#include "bits/stdc++.h"

signed main() {
	int n; std::cin >> n;

	std::vector <int> a(n + 1);
	for (int i = 1; i <= n; ++ i) {
		std::cin >> a[i];
	}

	std::cout << a[1] << " ";
	for (int i = 2; i <= n; ++ i) {
		if (a[i - 1] > a[i]) {
			for (int j = a[i - 1] - 1; j >= a[i]; -- j) 
				std::cout << j << " ";
		} else if (a[i - 1] < a[i]) {
			for (int j = a[i - 1] + 1; j <= a[i]; ++ j) 
				std::cout << j << " ";
		} else {
			std::cout << a[i] + 1 << " " << a[i] << " ";
		}
	}
}

C

Problem/题意

给两个由 小写字母 和 @ 组成的字符串,其中 @ 可以用(a、t、c、o、d、e、r)替换,问替换后两个串能否一致。

Thought/思路

先记录两个串中的字符出现的次数cnt,然后把相同的先减去。

然后两个 cnt 数组分别判断:如判断S串,当遇到某个字符的 cnt 不为 0,就去查看这个字符能否由 @ 转换,并且要求 T 串的 @ 的 cnt 大于等于 这个字符的 cnt。

Code/代码

#include "bits/stdc++.h"

int cnts[27], cntt[27];

signed main() {
	std::string s; std::cin >> s;
	std::string t; std::cin >> t;

	for (int i = 0; i < s.length(); ++ i) {
		if (s[i] == '@') cnts[26] ++;
		else cnts[s[i] - 'a'] ++;

		if (t[i] == '@') cntt[26] ++;
		else cntt[t[i] - 'a'] ++;
	}

	
	for (int i = 0; i < 26; ++ i) { // @ 不用减
		int tmp = std::min(cntt[i], cnts[i]);
		cntt[i] -= tmp;
		cnts[i] -= tmp;
	}

	// for (int i = 0; i <= 26; ++ i) std::cout << cnts[i] << " ";
	// std::cout << "\n";
	// for (int i = 0; i <= 26; ++ i) std::cout << cntt[i] << " ";
	// std::cout << "\n";

	int get[7] = {'a' - 'a', 't' - 'a', 'c' - 'a', 'o' - 'a', 'd' - 'a', 'e' - 'a', 'r' - 'a'};

	bool ans = true;
	for (int i = 0; i < 26; ++ i) {
		if (cnts[i] > 0) {
			bool flag = false;
			for (int j = 0; j < 7; ++ j) {
				if (get[j] == i) flag = true;
			}
			if (flag and cntt[26] >= cnts[i]) {
				cntt[26] -= cnts[i];
			} else {
				ans = false;
				//std::cout << "error in s\n";
			}
		}
	}

	for (int i = 0; i < 26; ++ i) {
		if (cntt[i] > 0) {
			bool flag = false;
			for (int j = 0; j < 7; ++ j) {
				if (get[j] == i) flag = true;
			}
			if (flag and cnts[26] >= cntt[i]) {
				cnts[26] -= cntt[i];
			} else {
				ans = false;
				//std::cout << "error in t\n";
			}
		}
	}

	if (ans) std::cout << "Yes\n";
	else std::cout << "No\n";
}

D

Problem/题意

给一个由 0、1、? 组成的字符串,给一个数 n,问:将 ?换成 0 或 1 后,最接近(<=)n 的数是多少?若没有,输出 -1。

Thought/思路

显然是贪心,先把原来的1全部算上,然后判断一次是否 <= n,若 <= n,则从最高位开始判断。

若该位是 ?,且置为 1 后仍 <= n,那么这一位就可以变成 1。

注意:

最好 reverse 一下字符串。

Code/代码

#include "bits/stdc++.h"

#define int long long

int n;
std::string t;
char s[70]; 

signed main() {
	std::cin >> t >> n;

	for (int i = t.length() - 1; i >= 0; -- i) {
		s[t.length() - i - 1] = t[i];
	}

	int tmp = 0;
	for (int i = 0; i < t.length(); ++ i) {
		if (s[i] == '1') {
			tmp = tmp | (1ll << i);
		}
	}

	if (tmp > n) {
		std::cout << -1;
		return 0;
	}

	std::vector <int> v;
	v.push_back(tmp);
	for (int i = t.length() - 1; i >= 0; -- i) {
		if (s[i] == '?') {
			if ((tmp | (1ll << i)) <= n) {
				tmp = tmp | (1ll << i);
				v.push_back(tmp);
			}
		}
	}

	std::cout << v[v.size() - 1] << "\n";
}

E

Problem/题意

给一个矩阵,上面有“起点”、“终点”、“墙”、“糖”。在最多行走 T 次的条件下,能最多吃到多少颗糖。

Thought/思路

1.注意到糖的数量很少,最多18颗。

因此,我们可以用 18 个二进制位,来表示“糖的获取情况”。比如一共有 5 颗糖,此时糖的获取情况 num = 10,而 10 转换成二进制数就是 01010,这就表示,第二、四颗糖已经获取了。

2.用 dp[num][i] 表示“从起点出发,当前获取的糖的情况为 s,此时停留在18颗糖中的第 i 颗糖”的最短距离。

3.所以,最后只要求出每颗糖到终点的最短距离 dis[i][end],然后判断每个 dp[num][i] + dis[i][end] <= T 即可。

4.所以,最难的问题就在于,dp应该怎么更新。

在这之前,我们用18次“bfs”求出“每颗糖果到其他所有点的距离”,如:dis[i][x][y]表示“糖 i 到点(x,y)的距离”。可见,dis需要用一个三维数组存储。

最简单也是最初始的情况是:“从起点出发,每种情况都只获取了一颗糖”。即:二进制数上只有一个“1”。也就是令:

dp[1 << i][i] = dis[i][sx][sy],(i = 0,1,2,.....,17)

因此,肯定是用糖果少的情况,去拓展糖果多的情况。

比如:

当我们确定一个“糖果获取情况num”和“当前所在的糖果j”,那么,我们只需要遍历一遍num的二进制位,就能知道:二进制位为“1”糖果已经包含在“num”中,二进制位为“0”的糖果没有包含在“num”中。

选择没有包含在“num”中的糖果进行拓展,此时判断:从第j颗糖果出发,加上“第j颗糖果到第k颗糖果的最短路”(之前的bfs已经求出了),能否比dp[num | (1 << k)][k]更小,若更小,则更新。

加以代码理解:

for (int i = 1; i <= (1ll << cnt) - 1; ++ i) { // cnt是糖果数量
		for (int j = 0; j < cnt; ++ j) {
			if (dp[i][j] == INF) continue; // 说明这种情况还没有遇到过,不能用
			for (int k = 0; k < cnt; ++ k) {
				if ((i >> k) & 1) continue; // 已经包含的,跳过
				int x = candy[k].first, y = candy[k].second;
				if (dp[i | (1 << k)][k] > dp[i][j] + dis[j][x][y]) {
					dp[i | (1 << k)][k] = dp[i][j] + dis[j][x][y];
				}
			}
		}
	}

Code/代码

#include "bits/stdc++.h"

typedef std::pair <int, int> pii;

const int INF = (int)1e9;

int h, w, t, vis[307][307];

char mp[307][307];

int dir[5] = {0, 1, 0, -1, 0};

std::vector <pii> candy;

std::vector <std::vector <int>> dis[20];

bool check(int x, int y) {
	if (x < 1 or x > h or y < 1 or y > w or mp[x][y] == '#' or vis[x][y]) return true;
	else return false;
}

std::vector <std::vector <int>> bfs(int x, int y) {
	std::vector <std::vector <int>> res(307, std::vector <int> (307, INF));

	memset(vis, 0, sizeof vis);

	std::queue <pii> q;
	q.push({x, y});

	res[x][y] = 0;

	while (!q.empty()) {
		auto top = q.front(); q.pop();

		int x = top.first, y = top.second;

		if (vis[x][y]) continue;
		vis[x][y] = 1;

		for (int i = 0; i <= 3; ++ i) {
			int nx = x + dir[i], ny = y + dir[i + 1];
			if (check(nx, ny)) continue;
			if (res[nx][ny] > res[x][y] + 1) {
				res[nx][ny] = res[x][y] + 1;
				q.push({nx, ny});
			}
		}
	}

	return res;
}

signed main() {
	std::cin >> h >> w >> t;

	int sx = 0, sy = 0; // 起点
	int ex = 0, ey = 0; // 终点

	for (int i = 1; i <= h; ++ i) {
		for (int j = 1; j <= w; ++ j) {
			std::cin >> mp[i][j];
			if (mp[i][j] == 'S') {
				sx = i, sy = j;
			}
			if (mp[i][j] == 'o') {
				candy.push_back({i, j});
			}
			if (mp[i][j] == 'G') {
				ex = i, ey = j;
			}
		}
	}

	int cnt = candy.size();
	for (int i = 0; i < cnt; ++ i) {
		dis[i] = bfs(candy[i].first, candy[i].second); // 求出糖果i到其他位置的最短距离
	}

	std::vector <std::vector <int>> dp(1ll << cnt, std::vector <int>(20, INF));
	
	for (int i = 0; i < cnt; ++ i) {
		dp[1ll << i][i] = dis[i][sx][sy]; // 初始化每个糖果到起点的距离
	}

	for (int i = 1; i <= (1ll << cnt) - 1; ++ i) {
		for (int j = 0; j < cnt; ++ j) {
			if (dp[i][j] == INF) continue;
			for (int k = 0; k < cnt; ++ k) {
				if ((i >> k) & 1) continue;
				int x = candy[k].first, y = candy[k].second;
				if (dp[i | (1 << k)][k] > dp[i][j] + dis[j][x][y]) {
					dp[i | (1 << k)][k] = dp[i][j] + dis[j][x][y];
				}
			}
		}
	}

	int ans = -1;
	std::vector <std::vector <int>> none = bfs(sx, sy); // 计算起点到其他点距离
	if (none[ex][ey] <= t) ans = 0; // 能到终点


	for (int i = 1; i <= (1ll << cnt) - 1; ++ i) {
		for (int j = 0; j < cnt; ++ j) {
			if (dp[i][j] + dis[j][ex][ey] <= t) {
				int num = 0;
				for (int k = 0; k < cnt; ++ k) {
					if ((i >> k) & 1) num ++;
				}
				ans = std::max(ans, num);
			}
		}
	}

	std::cout << ans;

}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值