引言
这几天开始在 d e v c p p devcpp devcpp 上编写代码了,然后复制代码到 O J OJ OJ 上去测评,然后就是非常的不适应,代码环境、背景颜色、编译选项的一些设置啥的都很不好,而且也错的很多,主要是一些低级错误,所以说细节还是不到位,因为蓝桥杯是以最后一次提交为准,而且也不会给你任何反馈,所以说自己得提前适应这个环境和比赛规则,加油!
一、奶牛选美
标签:BFS
思路:
题目要求从一个斑点到另一个斑点地最短距离,由于
N
≤
50
N \leq 50
N≤50 ,所以我们可以直接枚举一块斑点中的所有的点,看最小的碰到斑点地最短距离是多少。具体做法,我是先枚举了一块斑点,在
s
t
st
st 数组中标记了,然后遍历这块斑点的所有点,取第一次碰到另一块斑点(最短距离)的最小值即可。具体细节可以见代码。
题目描述:
听说最近两斑点的奶牛最受欢迎,约翰立即购进了一批两斑点牛。
不幸的是,时尚潮流往往变化很快,当前最受欢迎的牛变成了一斑点牛。
约翰希望通过给每头奶牛涂色,使得它们身上的两个斑点能够合为一个斑点,让它们能够更加时尚。
牛皮可用一个 N×M 的字符矩阵来表示,如下所示:
................
..XXXX....XXX...
...XXXX....XX...
.XXXX......XXX..
........XXXXX...
.........XXX....
其中,X 表示斑点部分。
如果两个 X 在垂直或水平方向上相邻(对角相邻不算在内),则它们属于同一个斑点,由此看出上图中恰好有两个斑点。
约翰牛群里所有的牛都有两个斑点。
约翰希望通过使用油漆给奶牛尽可能少的区域内涂色,将两个斑点合为一个。
在上面的例子中,他只需要给三个 . 区域内涂色即可(新涂色区域用 ∗ 表示):
................
..XXXX....XXX...
...XXXX*...XX...
.XXXX..**..XXX..
........XXXXX...
.........XXX....
请帮助约翰确定,为了使两个斑点合为一个,他需要涂色区域的最少数量。
输入格式
第一行包含两个整数 N 和 M。
接下来 N 行,每行包含一个长度为 M 的由 X 和 . 构成的字符串,用来表示描述牛皮图案的字符矩阵。
输出格式
输出需要涂色区域的最少数量。
数据范围
1≤N,M≤50
输入样例:
6 16
................
..XXXX....XXX...
...XXXX....XX...
.XXXX......XXX..
........XXXXX...
.........XXX....
输出样例:
3
示例代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
const int N = 100;
int n, m;
char g[N][N];
bool st[N][N];
int dist[N][N];
int dir[4][2] = {0,1,0,-1,1,0,-1,0};
void bfs1(PII S)
{
st[S.first][S.second] = true;
queue<PII> q; q.push({S.first, S.second});
while(q.size())
{
auto t = q.front(); q.pop();
for(int i = 0; i < 4; ++i)
{
int x = t.first + dir[i][0];
int y = t.second + dir[i][1];
if(x < 0 || x >= n || y < 0 || y >= m) continue;
if(st[x][y] || g[x][y] == '.') continue;
st[x][y] = true;
q.push({x,y});
}
}
}
int bfs2(PII S)
{
memset(dist, -1, sizeof dist);
dist[S.first][S.second] = 0;
queue<PII> q; q.push({S.first, S.second});
while(q.size())
{
auto t = q.front(); q.pop();
for(int i = 0; i < 4; ++i)
{
int x = t.first + dir[i][0];
int y = t.second + dir[i][1];
if(x < 0 || x >= n || y < 0 || y >= m) continue;
if(dist[x][y] != -1 || st[x][y]) continue;
dist[x][y] = dist[t.first][t.second] + 1;
if(g[x][y] == 'X') return dist[x][y];
q.push({x,y});
}
}
return 1e9;
}
int main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n >> m;
for(int i = 0; i < n; ++i) cin >> g[i];
for(int i = 0; i < n; ++i)
{
bool flag = false;
for(int j = 0; j < m; ++j)
{
if(g[i][j] == 'X')
{
bfs1({i,j});
flag = true; break;
}
}
if(flag) break;
}
int res = 1e9;
for(int i = 0; i < n; ++i)
{
for(int j = 0; j < m; ++j)
{
if(st[i][j])
{
res = min(res, bfs2({i,j}));
}
}
}
cout << res - 1 << endl;
return 0;
}
二、树的重心
标签:DFS
思路:
题目要求找到树的重心的那个值,也就是删掉该点后的连通块中的最大值最小。那么我们可以一一枚举所有的点,然后找到连通块最大值最小的即可。我们用递归来做,
d
f
s
(
u
)
dfs(u)
dfs(u) 返回的是包括结点
u
u
u 在内的所有孩子的总和。那么我们先是遍历每一个孩子,递归找出他们的连通块,并且因为是从根结点开始递归的,对于该结点以上的结点,我们可以记录一个
s
u
m
sum
sum ,最后拿总和减去再加上所有孩子的连通块,就可以求出删除该结点的每一个连通块的数量,取最小值即可。更多细节见代码。
题目描述:
给定一颗树,树中包含 n 个结点(编号 1∼n)和 n−1 条无向边。
请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。
重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。
输入格式
第一行包含整数 n,表示树的结点数。
接下来 n−1 行,每行包含两个整数 a 和 b,表示点 a 和点 b 之间存在一条边。
输出格式
输出一个整数 m,表示将重心删除后,剩余各个连通块中点数的最大值。
数据范围
1≤n≤105
输入样例
9
1 2
1 7
1 4
2 8
2 5
4 3
3 9
4 6
输出样例:
4
示例代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
const int N = 1e5+10, M = N * 2;
int n;
int h[N], e[M], ne[M], idx;
bool st[N];
int ans = 2e9;
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int dfs(int u)
{
st[u] = true;
int sum = 1, size = 0;
for(int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
if(st[j]) continue;
int t = dfs(j);
sum += t;
size = max(size, t);
}
size = max(size, n - sum);
ans = min(ans, size);
return sum;
}
int main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
memset(h, -1, sizeof h);
cin >> n;
for(int i = 0; i < n; ++i)
{
int a, b;
cin >> a >> b;
add(a,b), add(b,a);
}
dfs(1);
cout << ans << endl;
return 0;
}
三、大臣的旅费
标签:树的直径、BFS
思路:
要求花费最多,也就是求走的路最长, 然后根据数学关系就可以算出来了。求路最长,也就是求树的直径,公式就是先任意选取一个点,找到最远的点,然后从这个点开始再求一遍,这时最远的距离,就是它了。最远点我的可以用
B
F
S
BFS
BFS 做,在里面判断一下就行了,因为该图的边的权值都是正的且相等,所以能用
B
F
S
BFS
BFS。
题目描述:
很久以前,T 王国空前繁荣。
为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。
为节省经费,T 国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。
同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的。
J 是 T 国重要大臣,他巡查于各大城市之间,体察民情。
所以,从一个城市马不停蹄地到另一个城市成了 J 最常做的事情。
他有一个钱袋,用于存放往来城市间的路费。
聪明的 J 发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关。
具体来说,一段连续的旅途里,第 1 千米的花费为 11,第 2 千米的花费为 12,第 3 千米的花费为 13,…,第 x 千米的花费为 x+10。
也就是说,如果一段旅途的总长度为 1 千米,则刚好需要花费 11,如果一段旅途的总长度为 2 千米,则第 1 千米花费 11,第
2 千米花费 12,一共需要花费 11+12=23。
J 大臣想知道:他从某一个城市出发,中间不休息,到达另一个城市,所有可能花费的路费中最多是多少呢?
输入格式
输入的第一行包含一个整数 n,表示包括首都在内的 T 王国的城市数。
城市从 1 开始依次编号,1 号城市为首都。
接下来 n−1 行,描述 T 国的高速路(T 国的高速路一定是 n−1 条)。
每行三个整数 Pi,Qi,Di,表示城市 Pi 和城市 Qi 之间有一条双向高速路,长度为 Di 千米。
输出格式
输出一个整数,表示大臣 J 最多花费的路费是多少。
数据范围
1≤n≤105,1≤Pi,Qi≤n,1≤Di≤1000
输入样例:
5
1 2 2
1 3 1
2 4 5
2 5 4
输出样例:
135
示例代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
const int N = 1e5+10, M = N * 2, INF = 0x3f3f3f3f;
int n, m;
int h[N], e[M], w[M], ne[M], idx;
int dist[N];
int maxv = -1, maxid = 0;
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
void bfs(int S)
{
memset(dist, -1, sizeof dist);
dist[S] = 0;
queue<int> q; q.push(S);
while(q.size())
{
int t = q.front(); q.pop();
for(int i = h[t]; i != -1; i = ne[i])
{
int j = e[i];
if(dist[j] != -1) continue;
dist[j] = dist[t] + w[i];
if(dist[j] > maxv) maxv = dist[j], maxid = j;
q.push(j);
}
}
}
int main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
memset(h,-1,sizeof h);
cin >> n;
for(int i = 0; i < n - 1; ++i)
{
int a, b, c; cin >> a >> b >> c;
add(a,b,c), add(b,a,c);
}
bfs(1);
bfs(maxid);
LL res = 10ll * maxv + (1ll + maxv) * maxv / 2;
cout << res << endl;
return 0;
}
四、迷宫
标签:BFS
思路:
就是一个
B
F
S
BFS
BFS 模板题,没啥说的,值得注意的是,这道题的起点或者终点也有可能走不了,所以需要特判一下。
题目描述:
一天Extense在森林里探险的时候不小心走入了一个迷宫,迷宫可以看成是由 n∗n 的格点组成,每个格点只有2种状态,.和#,前者表示可以通行后者表示不能通行。
同时当Extense处在某个格点时,他只能移动到东南西北(或者说上下左右)四个方向之一的相邻格点上,Extense想要从点A走到
点B,问在不走出迷宫的情况下能不能办到。
如果起点或者终点有一个不能通行(为#),则看成无法办到。
注意:A、B不一定是两个不同的点。
输入格式
第1行是测试数据的组数 k,后面跟着 k 组输入。
每组测试数据的第1行是一个正整数 n,表示迷宫的规模是 n∗n 的。
接下来是一个 n∗n 的矩阵,矩阵中的元素为.或者#。
再接下来一行是 4 个整数 ha,la,hb,lb,描述 A 处在第 ha 行, 第 la 列,B 处在第 hb 行, 第 lb 列。
注意到 ha,la,hb,lb 全部是从 0 开始计数的。
输出格式
k行,每行输出对应一个输入。
能办到则输出“YES”,否则输出“NO”。
数据范围
1≤n≤100
输入样例:
2
3
.##
..#
#..
0 0 2 2
5
.....
###.#
..#..
###..
...#.
0 0 4 0
输出样例:
YES
NO
示例代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
const int N = 110;
int T;
int n, m;
char g[N][N];
bool st[N][N];
PII S, E;
int dir[4][2] = {0,1,0,-1,1,0,-1,0};
bool bfs()
{
if(g[S.first][S.second] == '#' || g[E.first][E.second] == '#') return false;
memset(st, 0, sizeof st);
st[S.first][S.second] = true;
queue<PII> q; q.push(S);
while(q.size())
{
auto t = q.front(); q.pop();
for(int i = 0; i < 4; ++i)
{
int x = t.first + dir[i][0];
int y = t.second + dir[i][1];
if(x < 0 || x >= n || y < 0 || y >= n) continue;
if(st[x][y] || g[x][y] == '#') continue;
st[x][y] = true;
q.push({x,y});
}
}
return st[E.first][E.second];
}
int main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> T;
while(T--)
{
cin >> n;
for(int i = 0; i < n; ++i) cin >> g[i];
cin >> S.first >> S.second >> E.first >> E.second;
if(bfs()) puts("YES");
else puts("NO");
}
return 0;
}