今日刷题:\(1005-1008\)
1005 NC15196 迷宫2
题目描述
这是一个关于二维格子状迷宫的题目。迷宫的大小为 \(N*M\),左上角格子座标为 \((1,1)\)、右上角格子座标为 \((1,M)\)、左下角格子座标为 \((N,1)\)、右下角格子座标为 \((N,M)\)。每一格都用 \(-1\) 到 \(10^9\) 之间的整数表示,意义分别为:\(-1\) 为墙壁,\(0\) 为走道,而 \(1\) 到 \(10^9\) 之间的正整数代表特殊的走道。
蜥蜴最初位于迷宫的座标 \((1,1)\) 的格子,每一步蜥蜴只能往上、下、左、右、左上、右上、左下、右下八个方向之一前进一格,并且,他也不能走出迷宫边界。蜥蜴的目的地是走到迷宫的右下角格子,也就是座标位置 \((N,M)\)。我们想要动一些手脚,使得蜥蜴没有办法从 \((1,1)\) 出发并抵达 \((N,M)\)。我们学会了一个邪恶的法术,这个法术可以把特殊的走道变成墙壁,施法一次的代价为表示该特殊走道的正整数。
假设,我们可以在蜥蜴出发之前不限次数的使用这个邪恶的法术,所花的总代价即为每次施法代价的总和,蜥蜴出发之后就不能再使用这个法术了,请问让蜥蜴没办法达到终点所必须花费的最小总代价是多少呢?
注意,\(0\) 所代表的走道是无法变为墙壁的。
输入描述
输入的第一行有三个正整数 \(Q,N,M\)。
代表接下来有 \(Q\) 组数据,这 \(Q\) 组数据都是 \(N*M\) 的迷宫。
接下来每组数据各 \(N\) 行,代表一个迷宫,每行各 \(M\) 个整数,第 \(i\) 行中的第 \(j\) 个整数代表迷宫座标 \((i,j)\) 的格子。
输出描述
每一组数据输出一行,如果无论如何蜥蜴都能到达终点,请在这一行中输出 \(-1\),否则请在这一行中输出一个代表答案的整数。
示例
输入
3 3 3
0 2 2
3 2 3
2 2 0
0 1 2
-1 1 -1
2 1 0
0 1 2
0 0 0
2 1 0
输出
6
1
-1
备注
\(1<=Q<=5*10^3\)
\(1<=Q*N*M<=2.5*10^5\)
\(1<=N,M<=500\)
代表迷宫格子的数字为介于 \(-1\) 和 \(10^9\) 间的整数(包含 \(-1\) 和 \(10^9\))
每个迷宫中,代表座标 \((1,1)\) 和 \((N,M)\) 的格子的数字一定是 \(0\)
解题思路
需要找出一条路径使得其花费最小且能够阻挡蜥蜴从左上角走到右下角。
因为蜥蜴可以斜着走,故在找路径时,需要避免出现路径中的格子仅一个点相连的情况(这样可以使得蜥蜴走过去),因此在寻找答案时只能走上下左右四个方向。
这样的最短路径可以将起点和终点分隔开,因此从第一行或者最后一列开始往左下方寻找。
对于输入的处理:因为 \(0\) 所代表的格子无法变为墙壁,故代价设为正无穷,而本来就是墙的(即 \(-1\)),其代价为 \(0\)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 510;
const LL INF = 0x3f3f3f3f3f3f3f3f;
int q, n, m;
LL g[N][N];
bool st[N][N];
int dx[] = {-1, 0, 1, 0}, dy[] = {0, -1, 0, 1};
LL bfs() {
LL ans = 1e18;
priority_queue<pair<LL, pair<int, int>>,
vector<pair<LL,pair<int, int>>>,
greater<pair<LL,pair<int, int>>>> q;
for (int j = 2; j <= m; j++)
if (g[1][j] != INF)
q.push({g[1][j], {1, j}});
for (int i = 2; i < n; i++)
if (g[i][m] != INF)
q.push({g[i][m], {i, m}});
while (q.size()) {
auto t = q.top();
q.pop();
int x = t.second.first, y = t.second.second;
if (x == n || y == 1) {
ans = min(ans, t.first);
continue;
}
if (st[x][y])
continue;
st[x][y] = true;
for (int i = 0; i < 4; i++) {
int a = x + dx[i], b = y + dy[i];
if (a >= 1 && a <= n && b >= 1 && b <= m && g[a][b] != INF) {
q.push({t.first + g[a][b], {a, b}});
}
}
}
return ans;
}
int main() {
scanf("%d%d%d", &q, &n, &m);
while (q--) {
memset(st, false, sizeof st);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
scanf("%lld", &g[i][j]);
if (g[i][j] == -1)
g[i][j] = 0;
else if (g[i][j] == 0)
g[i][j] = INF;
}
LL ans = bfs();
if (ans == 1e18)
ans = -1;
printf("%lld\n", ans);
}
return 0;
}
1006 NC15479 最短路
题目描述
企鹅国中有 \(N\) 座城市,编号从 \(1\) 到 \(N\)。
对于任意的两座城市 \(i\) 和 \(j\),企鹅们可以花费 \((i\, xor\, j)∗C\) 的时间从城市 \(i\) 走到城市 \(j\),这里 \(C\) 为一个给定的常数。
当然除此之外还有 \(M\) 条单向的快捷通道,第i条快捷通道从第 \(F_i\) 个城市通向第 \(T_i\) 个城市,走这条通道需要消耗 \(V_i\) 的时间。
现在来自 \(Penguin\, Kingdom\, University\) 的企鹅豆豆正在考虑从城市 \(A\) 前往城市 \(B\) 最少需要多少时间?
输入描述
输入第一行包含三个整数 \(N,M,C\),表示企鹅国城市的个数、快捷通道的个数以及题面中提到的给定的常数 \(C\)。
接下来的 \(M\) 行,每行三个正整数 \(F_i,T_i,V_i(1≤F_i≤N,1≤T_i≤N,1≤V_i≤100)\),分别表示对应通道的起点城市标号、终点城市标号和通过这条通道需要消耗的时间。
最后一行两个正整数 \(A,B(1≤C≤100)\),表示企鹅豆豆选择的起点城市标号和终点城市标号。
输出描述
输出一行一个整数,表示从城市 \(A\) 前往城市 \(B\) 需要的最少时间。
示例1
输入
4 2 1
1 3 1
2 4 4
1 4
输出
5
说明:直接从 \(1\) 走到 \(4\) 就好了。
示例2
输入
7 2 10
1 3 1
2 4 4
3 6
输出
34
说明:先从 \(3\) 走到 \(2\),再从 \(2\) 通过通道到达 \(4\),再从 \(4\) 走到 \(6\)。
备注

解题思路
参考思路:刷题记录:牛客NC15479最短路和NC15479 最短路
这里解释一下建边时的边界情况:因为 \(i\bigoplus j\) 可能会为 \(0\),所有从 \(0\) 开始,引入一个虚拟的点。而 \(i\bigoplus j\) 可能会超过 \(n\),比如 \(i=(0111)_2,j=(1000)_2\),而 \(n=10\),则 \(i\bigoplus j=(1111)_2=15>n\)。
C++ 代码
// NC15479
#include <bits/stdc++.h>
using namespace std;
const int N = 100010, M = 500010 + N * 32;
int n, m, C, A, B;
int h[N], e[M], ne[M], w[M], idx;
int dist[N], st[N];
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
int main() {
memset(h, -1, sizeof h);
memset(dist, 0x3f, sizeof dist);
memset(st, 0, sizeof st);
scanf("%d%d%d", &n, &m, &C);
for (int i = 0; i <= n; i++)
for (int j = 1; j <= n; j <<= 1) {
if ((i ^ j) > n) continue;
add(i, i ^ j, j * C);
}
for (int i = 0; i < m; i++) {
int f, t, v;
scanf("%d%d%d", &f, &t, &v);
add(f, t, v);
}
scanf("%d%d", &A, &B);
dist[A] = 0;
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;
q.push({dist[A], A});
while (q.size()) {
auto t = q.top();
q.pop();
int ver = t.second;
if (st[ver]) continue;
st[ver] = true;
for (int i = h[ver]; ~i; i = ne[i]) {
int j = e[i];
if (dist[j] > dist[ver] + w[i]) {
dist[j] = dist[ver] + w[i];
q.push({dist[j], j});
}
}
}
printf("%d\n", dist[B]);
return 0;
}
1007 NC17511 公交路线
题目描述
\(P\) 市有 \(n\) 个公交站,之间连接着 \(m\) 条道路。\(P\) 市计划新开设一条公交线路,该线路从城市的东站(\(s\) 点)修建到西站(\(t\) 点),请为 \(P\) 市设计一条满足上述条件并且最短的公交线路图。
输入描述
第一行有 \(4\) 个正整数 \(n\),\(m\),\(s\),\(t\)。
接下来 \(m\) 行,每行 \(3\) 个数 \(a,b,v\) 描述一条无向道路 \(a—b\),长度为 \(v\)。
输出描述
如果有解,输出一行,表示满足条件的最短公交线路的长度 \(c\)。否则,输出 \(-1\)。
示例1
输入
3 3 1 2
1 2 3
2 3 4
1 3 5
输出
3
示例2
输入
3 3 1 2
1 2 5
2 3 3
1 3 1
输出
4
示例3
输入
3 1 1 1
1 2 1
输出
0
备注
对于 \(100\%\) 的测试数据:\(1 ≤ s,t ≤ n ≤ 1000,1 ≤ m ≤ 10000\),\(1≤\) 道路的长度 \(≤10000\)。
解题思路
Dijkstra 算法模板题。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1010, M = 20010;
int n, m, s, t;
bool st[N];
int dist[N];
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx++;
}
int main() {
memset(h, -1, sizeof h);
memset(dist, 0x3f, sizeof dist);
scanf("%d%d%d%d", &n, &m, &s, &t);
while (m--) {
int a, b, v;
scanf("%d%d%d", &a, &b, &v);
add(a, b, v);
add(b, a, v);
}
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;
dist[s] = 0;
q.push({dist[s], s});
while (q.size()) {
auto tmp = q.top();
q.pop();
int d = tmp.first, ver = tmp.second;
if (st[ver]) continue;
st[ver] = true;
for (int i = h[ver]; ~i; i = ne[i]) {
int j = e[i];
if (dist[j] > w[i] + dist[ver]) {
dist[j] = w[i] + dist[ver];
q.push({dist[j], j});
}
}
}
if (dist[t] == 0x3f3f3f3f) dist[t] = -1;
printf("%d\n", dist[t]);
return 0;
}
1008 NC17509 挖沟
题目描述
胡队长带领 \(HA\) 实验的战士们玩真人 \(CS\),真人 \(CS\) 的地图由一些据点组成,现在胡队长已经占领了 \(n\) 个据点,为了方便,将他们编号为 \(1-n\),为了隐蔽,胡队长命令战士们在每个据点出挖一个坑,让战士们躲在坑里。由于需要在任意两个点之间传递信息,两个坑之间必须挖出至少一条通路,而挖沟是一件很麻烦的差事,所以胡队长希望挖出数量尽可能少的沟,使得任意两个据点之间有至少一条通路,顺便,尽可能的 \(∑d[i][j]\) 使最小(其中 \(d[i][j]\) 为据点 \(i\) 到 \(j\) 的距离)。
输入描述
第一行有 \(2\) 个正整数 \(n\),\(m\),\(m\) 表示可供挖的沟数。
接下来 \(m\) 行,每行 \(3\) 个数 \(a,b,v\),每行描述一条可供挖的沟,该沟可以使 \(a\) 与 \(b\) 连通,长度为 \(v\)。
输出描述
输出一行,一个正整数,表示要使得任意两个据点之间有一条通路,至少需要挖长的沟。(数据保证有解)
示例1
输入
2 2
1 2 1
1 2 3
输出
1
示例2
输入
3 3
1 2 3
2 3 4
1 3 5
输出
7
备注
对于 \(100\%\) 的测试数据:\(1 ≤ n ≤ 100000,1 ≤ m ≤ 500000,1 ≤ v ≤ 10000\)
解题思路
最小生成树模板题。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100010, M = 500010;
int n, m;
struct Edge {
int x, y, w;
bool operator< (const Edge &W) const {
return w < W.w;
}
} edges[M];
int p[N];
int find(int x) {
if (p[x] != x)
p[x] = find(p[x]);
return p[x];
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
p[i] = i;
for (int i = 0; i < m; i++) {
int a, b, v;
scanf("%d%d%d", &a, &b, &v);
edges[i] = {a, b, v};
}
sort(edges, edges + m);
LL ans = 0, cnt = 0;
for (int i = 0; i < m; i++) {
int x = edges[i].x, y = edges[i].y;
int px = find(x), py = find(y);
if (p[x] != p[y]) {
p[px] = py;
cnt++;
ans += edges[i].w;
}
if (cnt == n - 1)
break;
}
printf("%lld\n", ans);
return 0;
}
本文来自博客园,作者:Cocoicobird,转载请注明原文链接:https://www.cnblogs.com/Cocoicobird/p/19689985
浙公网安备 33010602011771号