武汉工程大学第三届ACM程序设计选拔赛(正式赛)题解
Posted 康次学长
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了武汉工程大学第三届ACM程序设计选拔赛(正式赛)题解相关的知识,希望对你有一定的参考价值。
题目链接:https://ac.nowcoder.com/acm/contest/16172
感谢牛客网为此次比赛提供在线评测环境
A. 疯狂动物城
此题改至ACWing 240 食物链
此题知识点:带权并查集
#include <iostream>
using namespace std;
const int N = 5e4 + 5, mod = 4;
int n, m, cnt;
int d[N], pre[N];
int find(int x)
{
if (x != pre[x]) {
int root = find(pre[x]);
d[x] = (d[x] + d[pre[x]]) % mod;
pre[x] = root;
}
return pre[x];
}
int main()
{
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++)
pre[i] = i;
while (m--) {
int t, x, y;
scanf("%d %d %d", &t, &x, &y);
if (x < 1 || x > n || y < 1 || y > n || t != 1 && x == y) {
cnt++;
continue;
}
int k = t - 1;
int px = find(x), py = find(y);
if (px == py && ((d[x] - d[y]) % mod + mod) % mod != k) {
cnt++;
continue;
}
if (px != py) {
pre[px] = py;
d[px] = ((k - d[x] + d[y]) % mod + mod) % mod;
}
}
printf("%d\\n", cnt);
return 0;
}
方法二:扩展域并查集
作者不是很懂,只能把大佬的代码搬过来
https://ac.nowcoder.com/acm/contest/view-submission?submissionId=47751034
B. 密室逃脱
此题比较简单的做法是直接Dijkstra,如果传送门的两端都不是陷阱,并且不是相邻的点,那就将两端的边权设置为3。如果不习惯从下标0开始计数,可以先将所有的点的横纵坐标都+1,最后如果有输出点再将横纵坐标都-1
#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
typedef pair<int, int> PII;
typedef pair<int, PII> PIII;
const int N = 310;
const int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
int n, m, q;
int stx, sty, edx, edy;
char mp[N][N];
vector<PII> edges[N][N];
int dis[N][N];
bool vis[N][N];
int Dijkstra()
{
memset(dis, 0x3f, sizeof dis);
priority_queue<PIII, vector<PIII>, greater<PIII>> heap;
heap.push({0, {stx, sty}});
dis[stx][sty] = 0;
while (!heap.empty()) {
int distance = heap.top().first;
int a = heap.top().second.first, b = heap.top().second.second;
heap.pop();
if (vis[a][b])
continue;
vis[a][b] = true;
if (a == edx && b == edy)
return distance;
for (int i = 0; i < 4; i++) {
int x = a + dx[i], y = b + dy[i];
if (!(x >= 1 && x <= n && y >= 1 && y <= m && mp[x][y] != '#' && dis[x][y] > distance + 1))
continue;
dis[x][y] = distance + 1;
heap.push({dis[x][y], {x, y}});
}
for (auto t : edges[a][b]) {
int x = t.first, y = t.second;
if (!(mp[x][y] != '#' && dis[x][y] > distance + 3))
continue;
dis[x][y] = distance + 3;
heap.push({dis[x][y], {x, y}});
}
}
return -1;
}
int main()
{
cin >> n >> m >> q;
for (int i = 1; i <= n; i++) {
cin >> mp[i] + 1;
for (int j = 1; j <= m; j++)
if (mp[i][j] == 'S')
stx = i, sty = j;
else if (mp[i][j] == 'T')
edx = i, edy = j;
}
while (q--) {
int a, b, c, d;
cin >> a >> b >> c >> d;
a++, b++, c++, d++;
edges[a][b].push_back({c, d});
edges[c][d].push_back({a, b});
}
cout << Dijkstra() << endl;
return 0;
}
C. 露营?料理!
此题的纸质版的地方由于工作人员疏忽,
∑
\\sum
∑的下标
i
=
0
i=0
i=0写成了
i
=
1
i=1
i=1,并且没有交代
w
0
w_0
w0恒等于0,对选手造成了干扰,在此感到非常抱歉。
此题知识点:前缀和+二分+双关键字排序。
前缀和求完之后需要把
w
0
w_0
w0的值放入到前缀和中,该元素的第二个关键字也要设置为0。
二分不是只有一种写法,在已经升序排列的数组
s
s
s中,求
s
i
≤
x
s_i \\le x
si≤x,
s
i
<
x
,
s
i
>
x
s_i < x,s_i > x
si<x,si>x,
s
i
≥
x
s_i \\ge x
si≥x 的下标
i
i
i,这四种二分写法是有区别的。进阶指南上面说只有10%的程序员会写二分
≥ x \\ge x ≥x的下界
int l = 1, r = n + 1;
a[n + 1] = 0x3f3f3f3f;
while (l < r) {
int mid = (l + r) >> 1;
if (a[mid] >= x) {
r = mid;
} else {
l = mid + 1;
}
}
if (l == n + 1) {
puts("Not Found");
} else {
printf("%d\\n", a[l]);
}
> x > x >x的下界
int l = 1, r = n + 1;
a[n + 1] = 0x3f3f3f3f;
while (l < r) {
int mid = (l + r) >> 1;
if (a[mid] > x) {
r = mid;
} else {
l = mid + 1;
}
}
if (l == n + 1) {
puts("Not Found");
} else {
printf("%d\\n", a[l]);
}
≤ x \\le x ≤x的上界
int l = 0, r = n;
a[0] = 0xc0c0c0c0;
while (l < r) {
int mid = (l + r + 1) >> 1;
if (a[mid] <= x) {
l = mid;
} else {
r = mid - 1;
}
}
if (l == 0) {
puts("Not Found");
} else {
printf("%d\\n", a[l]);
}
< x < x <x的上界
int l = 0, r = n;
a[0] = 0xc0c0c0c0;
while (l < r) {
int mid = (l + r + 1) >> 1;
if (a[mid] < x) {
l = mid;
} else {
r = mid - 1;
}
}
if (l == 0) {
puts("Not Found");
} else {
printf("%d\\n", a[l]);
}
此题标程
# include <iostream>
# include <cstdio>
# include <algorithm>
typedef long long ll;
const int N = 1e5 + 5;
int n;
struct Sum {
int ans;
int id;
const bool operator < (const Sum& rhs) const {
return ans < rhs.ans || (ans == rhs.ans && id < rhs.id);
}
};
int w[N];
Sum sum[N];
int main() {
int m;
std::cin >> n >> m;
for (int i = 1; i <= n; i++) {
scanf("%d", &w[i]);
}
for (int i = 1; i <= n; i++) {
sum[i].ans = sum[i-1].ans + w[i];
sum[i].id = i;
}
sum[n + 1].ans = sum[n + 1].id = 0;
std::sort(sum + 1, sum + 2 + n);
sum[0].ans = sum[0].id = 0xc0c0c0c0;
while (m--) {
int k;
scanf("%d", &k);
int l = 0;
int r = n + 1;
while (l < r) {
int mid = (l + r + 1) >> 1;
if (sum[mid].ans <= k) {
l = mid;
} else {
r = mid - 1;
}
}
if (l &以上是关于武汉工程大学第三届ACM程序设计选拔赛(正式赛)题解的主要内容,如果未能解决你的问题,请参考以下文章
2021-2022年度第三届全国大学生算法设计与编程挑战赛(冬季赛正式赛) 部分题题解
2021-2022年度第三届全国大学生算法设计与编程挑战赛(冬季赛正式赛) 部分题题解
个人赛组2021-2022年度第三届全国大学生算法设计与编程挑战赛(秋季赛)-正式赛