引言
今天主要复习了图论里的最短路、 B F S 、 D F S 、 F l o o d F i l l BFS、DFS、Flood\ Fill BFS、DFS、Flood Fill 算法,其实到现在感觉暴搜其实还挺好写的,现在刚好反过来了,不喜欢写 B F S BFS BFS 了,确实跟做的熟练程度有关,然后这几天就是好好休息,把状态调整好就行了,该复习的基本都复习的差不多了,只要考场发挥好状态即可。
一、母亲的牛奶
标签:BFS、DFS
思路:
这道题就是一个模拟的过程,在一个状态中有六种情况,
A
→
B
,
A
→
C
,
B
→
A
,
B
→
C
,
C
→
A
,
C
→
B
A\rightarrow B,A\rightarrow C,B\rightarrow A,B\rightarrow C,C\rightarrow A,C\rightarrow B
A→B,A→C,B→A,B→C,C→A,C→B ,然后判断最后的合法状态是否访问过,然后再次递归,就这样即可。最后把符合要求并且访问过的状态输出即可。
题目描述:
农夫约翰有三个容量分别为 A,B,C 升的挤奶桶。
最开始桶 A 和桶 B 都是空的,而桶 C 里装满了牛奶。
有时,约翰会将牛奶从一个桶倒到另一个桶中,直到被倒入牛奶的桶满了或者倒出牛奶的桶空了为止。
这一过程中间不能有任何停顿,并且不会有任何牛奶的浪费。
请你编写一个程序判断,当 A 桶是空的时候,C 桶中可能包含多少升牛奶,找出所有的可能情况。
输入格式
共一行,包含三个整数 A,B,C。
输出格式
共一行,包含若干个整数,表示 C 桶中牛奶存量的所有可能情况,请将这些数字按升序排列。
数据范围
1≤A,B,C≤20
输入样例1:
8 9 10
输出样例1:
1 2 8 9 10
输入样例2:
2 5 10
输出样例2:
5 6 7 8 9 10
示例代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y second
const int N = 25;
int A, B, C;
bool st[N][N][N];
void dfs(int a, int b, int c)
{
st[a][b][c] = true;
int s[3] = {a,b,c}, S[3] = {A,B,C};
for(int i = 0; i < 3; ++i)
{
for(int j = 0; j < 3; ++j)
{
if(i != j)
{
int t[3] = {a,b,c};
if(t[i] == 0 || t[j] == S[j]) continue;
int t3 = min(t[i], S[j] - s[j]);
t[i] -= t3, t[j] += t3;
if(st[t[0]][t[1]][t[2]]) continue;
dfs(t[0],t[1],t[2]);
}
}
}
}
int main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> A >> B >> C;
dfs(0,0,C);
for(int c = 0; c <= C; ++c)
{
for(int b = 0; b <= B; ++b)
{
if(st[0][b][c])
{
cout << c << " ";
break;
}
}
}
return 0;
}
二、奶牛回家
标签:最短路、Dijkstra、SPFA
思路:
核心问的是哪一头牛最快到达牛棚,可以看成从牛棚到每一头牛中的最短距离,实际就是个最短路问题。然后可以直接拿字符所对应的
A
S
C
L
L
ASCLL
ASCLL 码当作点,因为毕竟是拿邻接表存的也没什么,最后遍历一下所有牛初始的点,这个在输入的时候存一下就行了,然后找最小值即可,剩余的就是模板了,因为该题只有正权边,所以就拿
D
i
j
k
s
t
r
a
Dijkstra
Dijkstra 算法做了,因为
s
p
f
a
spfa
spfa 算法经常会卡常数,所以还是稳妥一点比较好。
题目描述:
晚餐时间马上就到了,奶牛们还在各自的牧场中悠闲的散着步。
当农夫约翰摇动铃铛,这些牛就要赶回牛棚去吃晚餐。
在吃晚餐之前,所有奶牛都在自己的牧场之中,有些牧场中可能没有奶牛。
每个牧场都通过一条条道路连接到一个或多个其他牧场(可能包括其自身)。
有时,两个(可能是相同的)牧场通过一条以上的道路相连。
至少存在一个牧场与牛棚通过一条道路直接相连。
所以说,所有奶牛都能够成功的从自己的牧场沿道路返回牛棚。
聪明的奶牛们总会选择最短的路径回到牛棚之中。
每条道路都是可以双向行走的,奶牛的行走速度也都一样。
我们用 a∼z 和 A∼Y 来标记所有的牧场。
所有用大写字母标记的牧场中都存在一头奶牛,所有用小写字母标记的牧场中都不存在奶牛。
牛棚的标记为 Z,这里最初是没有奶牛的。
现在你需要确定,哪一头奶牛能够最快到达牛棚,输出它最初所在的牧场的标记,并输出它走过的路径的长度。
注意,同一字母大小写标记的两个牧场(例如,牧场 A 和牧场 a)是两个完全不同的牧场。
输入格式
第一行包含整数 P,表示连接牧场以及牛棚的道路的条数。
接下来 P 行,每行包含两个字母以及一个整数,表示被一条道路连接的两个牧场的标记,以及这条道路的长度。
输出格式
输出一个字母和一个整数,表示最快回到牛棚的牛最初所在的牧场的标记以及它走过的路径的长度。
数据保证最快回到牛棚的牛只有一头。
数据范围
1≤P≤10000,所有道路长度均不超过 1000。
输入样例:
5
A d 6
B d 3
C e 9
d Z 8
e Z 3
输出样例:
B 11
示例代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y second
const int N = 150, M = 2e4+10, INF = 0x3f3f3f3f;
int n, m;
int h[N], e[M], w[M], ne[M], idx;
int dist[N];
bool st[N];
int start[N], cnt;
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
void dijkstra()
{
memset(dist, 0x3f, sizeof dist);
dist['Z'] = 0;
priority_queue<PII, vector<PII>, greater<PII>> heap;
heap.push({0,'Z'});
while(heap.size())
{
auto t = heap.top(); heap.pop();
int v = t.x, p = t.y;
if(st[p]) continue;
st[p] = true;
for(int i = h[p]; i != -1; i = ne[i])
{
int j = e[i];
if(dist[j] > dist[p] + w[i])
{
dist[j] = dist[p] + w[i];
heap.push({dist[j], j});
}
}
}
}
int main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
memset(h, -1, sizeof h);
int p; cin >> p;
while(p--)
{
char a, b; cin >> a >> b;
int c; cin >> c;
if(a >= 'A' && a < 'Z') start[cnt++] = a;
if(b >= 'A' && b < 'Z') start[cnt++] = b;
add(a,b,c), add(b,a,c);
}
dijkstra();
int minid, minv = INF;
for(int i = 0; i < cnt; ++i)
{
int j = start[i];
if(dist[j] < minv) minid = j, minv = dist[j];
}
printf("%c %d\n", minid, minv);
return 0;
}
三、扫雷
标签:BFS、DFS、Flood Fill
思路:
要找到最小的次数,做法:把所有的八个方向无地雷的格子给点了,因为会引起连带反应,然后再把剩余的还没点的格子一点就行了。先初始化一个
p
p
p 数组,代表八个方向的地雷的数量,然后依次遍历每个格子,只要
p
[
i
]
[
j
]
p[i][j]
p[i][j] 为零,那就
D
F
S
DFS
DFS ,然后把剩余没有遍历过的格子一加即可。
题目描述:
扫雷是一种计算机游戏,在 20 世纪 80 年代开始流行,并且仍然包含在某些版本的 Microsoft Windows 操作系统中。
在这个问题中,你正在一个矩形网格上玩扫雷游戏。
最初网格内的所有单元格都呈未打开状态。
其中 M 个不同的单元格中隐藏着 M 个地雷。
其他单元格内不包含地雷。
你可以单击任何单元格将其打开。
如果你点击到的单元格中包含一个地雷,那么游戏就会判定失败。
如果你点击到的单元格内不含地雷,则单元格内将显示一个 0 到 8 之间的数字(包括 0 和 8),这对应于该单元格的所有相邻单
元格中包含地雷的单元格的数量。
如果两个单元格共享一个角或边,则它们是相邻单元格。
另外,如果某个单元格被打开时显示数字 0,那么它的所有相邻单元格也会以递归方式自动打开。
当所有不含地雷的单元格都被打开时,游戏就会判定胜利。
例如,网格的初始状态可能如下所示(* 表示地雷,而 c 表示第一个点击的单元格):
*..*...**.
....*.....
..c..*....
........*.
..........
被点击的单元格旁边没有地雷,因此当它被打开时显示数字 0,并且它的 8 个相邻单元也被自动打开,此过程不断继续,最终状态如下:
*..*...**.
1112*.....
00012*....
00001111*.
00000001..
此时,仍有不包含地雷的单元格(用 . 字符表示)未被打开,因此玩家必须继续点击未打开的单元格,使游戏继续进行。
你想尽快赢得游戏胜利并希望找到赢得游戏的最低点击次数。
给定网格的尺寸(N×N),输出能够获胜的最小点击次数。
输入格式
第一行包含整数 T,表示共有 T 组测试数据。
每组数据第一行包含整数 N,表示游戏网格的尺寸大小。
接下来 N 行,每行包含一个长度为 N 的字符串,字符串由 .(无雷)和 *(有雷)构成,表示游戏网格的初始状态。
输出格式
每组数据输出一个结果,每个结果占一行。
结果表示为 Case #x: y,其中 x 是组别编号(从 1 开始),y 是获胜所需的最小点击次数。
数据范围
1≤T≤100,1≤N≤300
输入样例:
2
3
..*
..*
**.
5
..*..
..*..
.*..*
.*...
.*...
输出样例:
Case #1: 2
Case #2: 8
示例代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y second
const int N = 310;
int n, m;
char g[N][N];
bool st[N][N];
int p[N][N];
int dir[8][2] = {-1,-1,-1,0,-1,1, 0,-1,0,1, 1,-1,1,0,1,1};
void dfs(int x, int y)
{
int t = p[x][y];
p[x][y] = -1;
if(t) return;
for(int i = 0; i < 8; ++i)
{
int a = x + dir[i][0];
int b = y + dir[i][1];
if(a < 0 || a >= n || b < 0 || b >= n) continue;
dfs(a,b);
}
}
int main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int T; cin >> T;
for(int z = 1; z <= T; ++z)
{
cin >> n;
for(int i = 0; i < n; ++i) cin >> g[i];
for(int i = 0; i < n; ++i)
{
for(int j = 0; j < n; ++j)
{
if(g[i][j] != '.') p[i][j] = -1;
else
{
p[i][j] = 0;
for(int k = 0; k < 8; ++k)
{
int x = i + dir[k][0];
int y = j + dir[k][1];
if(x < 0 || x >= n || y < 0 || y >= n) continue;
if(g[x][y] == '*') p[i][j]++;
}
}
}
}
int res = 0;
for(int i = 0; i < n; ++i)
{
for(int j = 0; j < n; ++j)
{
if(p[i][j] == 0)
{
res++;
dfs(i,j);
}
}
}
for(int i = 0; i < n; ++i)
{
for(int j = 0; j < n; ++j)
{
if(p[i][j] != -1)
{
res++;
}
}
}
printf("Case #%d: %d\n", z, res);
}
return 0;
}