BFS例题
题目描述 原题链接
农夫知道一头牛的位置,想要抓住它。
农夫和牛都位于数轴上,农夫起始位于点 N,牛位于点 K。
农夫有两种移动方式:
从 X 移动到 X−1 或 X+1,每次移动花费一分钟
从 X 移动到 2∗X,每次移动花费一分钟
假设牛没有意识到农夫的行动,站在原地不动。
农夫最少要花多少时间才能抓住牛?
输入格式
共一行,包含两个整数N和K。
输出格式
输出一个整数,表示抓到牛所花费的最少时间。
数据范围
\(0≤N,K≤10^5\)
样例
输入: 5 17
输出:4
算法1
(BFS) \(O(K)\)
题目讲述一共有3种移动情况且权重一致
- 从 X 移动到 X−1
- 从 X 移动到 X+1
- 从 X 移动到 2∗X
相当于X 与X - 1,X + 1,2 * X ,各连一条边,从n开始进行bfs到k,dist[k]表示k点到n点的最短距离
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
const int N = 100010;
using namespace std;
bool st[N];
int dist[N],q[N];
int s,k;
int bfs()
{
int hh = 0, tt = 0;
memset(dist,0x3f,sizeof dist);
dist[s] = 0;
st[s] = true; q[0] = s;
while(hh <= tt)
{
int t = q[hh ++];
if(t == k) return dist[t];
if(2 * t < N && !st[2 * t]){
dist[2 * t] = dist[t] + 1;
q[++ tt] = t * 2;
st[2 * t] = true;
}
if(t + 1 < N && !st[t + 1]) {
dist[t + 1] = dist[t] + 1;
q[++ tt] = t + 1;
st[t + 1] = true;
}
if(t - 1 >= 0 && ! st[t - 1] ){
dist[t - 1] = dist[t] + 1;
q[++ tt] = t - 1;
st[t - 1] = true;
}
}
return dist[k];
}
int main()
{
cin >> s >> k;
cout << bfs() << endl;
}
题目描述 原题链接
题面翻译:
给你两个四位质数a,b;每次可以花1让a一个数位上的数字变换(0-9),但要保证每次变换后的数字还是一个素数,求问最少需要多少次变换让a变成b;
样例
输入:
3
1033 8179
1373 8017
1033 1033
输出:
6
7
0
算法1
(BFS)
四个数位,各有十个数字的变换可能,所以这题广搜的方向就是四十个。不断广搜直至找到要求的四位质数即可。
这道题思路并不难,但有几个麻烦;
- 质数的判断,每次搜出一个数后需要判断其是否为素数。我使用的方法是埃氏筛打表预处理一万以内的所有素数。
- 如何实现“变换”,为了方便,我采用的是字符串,也就是string类型来进行操作。使用字符串的好处,就是每次变换直接改变那个位置上的字符即可。用整型来操作会麻烦很多。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
const int N = 10010;
using namespace std;
bool st[N],vis[N];
int primes[N],cnt;
int dist[N],q[N];
int s,e;
// 筛素数,对应以后每个 st[x] 如果为 true表示素数,false表示合数
void init()
{
for(int i = 2; i < N; i ++)
{
if(! st[i]) primes[++ cnt] = i;
for(int j = 2; j <= N / i; j ++) st[i * j] = true;
}
}
// 字符转换操作
int change(char str[])
{
int ret = 0;
for(int i = 0; i < 4; i ++)
ret = ret * 10 + (str[i] - '0');
return ret;
}
int bfs()
{
int hh = 0, tt = 0;
q[0] = s;
memset(dist,0x3f,sizeof dist);
memset(vis,false,sizeof vis);
vis[s] = true;
dist[s] = 0;
while(hh <= tt)
{
int t = q[hh ++];
if(t == e) return dist[t];
// 字符操作
for(int i = 0; i < 4; i ++)
{
char str[4];
for(int k = 3,p = t; k >= 0; k --)
{
str[k] = (p % 10 + '0');
p /= 10;
}
// 枚举10种情况
for(int j = 0; j <= 9; j ++)
{
if(i == 0 && j == 0) continue;
str[i] = j + '0';
int x = change(str);
if(!st[x] && !vis[x])
{
vis[x] = true;
dist[x] = dist[t] + 1;
q[++ tt] = x;
}
}
}
}
return dist[e];
}
int main()
{
init();
int cases;
scanf("%d",&cases);
while(cases --)
{
scanf("%d%d",&s,&e);
cout << bfs() << endl;
}
}
题目描述 原题链接
题面翻译:
有两个容量分别为A,B的罐子,你有三种操作可以执行,1.灌满任意一个罐子 2.倒光任意一个罐子的水 3.将一个罐子的水倒到另一个罐子里。求最快需要倒几次让其中一罐的水量为C
样例
输入:
3 5 4
输出:
6
FILL(2)
POUR(2,1)
DROP(1)
POUR(2,1)
FILL(2)
POUR(2,1)
算法1
(BFS)
每一步都有六个方向,即:
"FILL(1)","FILL(2)","DROP(1)","DROP(2)","POUR(1,2)","POUR(2,1)"
求最快的方法所以用广搜,需要记录搜索路径,所以我采用了数组进行广搜。
思路并不难,具体实现较为麻烦:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <stack>
#define x first
#define y second
using namespace std;
string result[7];
bool st[110][110];
int dist[110][110];
typedef pair<int,int> PII;
PII q[10100];
const int INF = 0x3f3f3f3f;
void init()
{
result[1] = "FILL(1)";
result[2] = "FILL(2)";
result[3] = "POUR(1,2)";
result[4] = "POUR(2,1)";
result[5] = "DROP(1)";
result[6] = "DROP(2)";
}
int a,b,c;
struct Pre{
int x,y,t;
} pre[110][110];
int prea,preb;
int bfs()
{
memset(dist,0x3f,sizeof dist);
memset(st,false,sizeof st);
int hh = 0, tt = 0;
q[0] = {0,0};
dist[0][0] = 0;
st[0][0] = true;
while(hh <= tt)
{
PII t = q[hh ++];
if(t.x == c || t.y == c)
{
prea = t.x,preb = t.y;
return dist[t.x][t.y];
}
if(! st[a][t.y]) {
dist[a][t.y] = dist[t.x][t.y] + 1;
st[a][t.y] = true;
q[++ tt] = {a, t.y};
pre[a][t.y] = {t.x,t.y,1};
}
if(! st[t.x][b]) {
dist[t.x][b] = dist[t.x][t.y] + 1;
st[t.x][b] = true;
q[++ tt] = {t.x, b};
pre[t.x][b] = {t.x, t.y, 2};
}
if(! st[t.x][0]) {
dist[t.x][0] = dist[t.x][t.y] + 1;
st[t.x][0] = true;
q[++ tt] = {t.x, 0};
pre[t.x][0] = {t.x,t.y,6};
}
if(! st[0][t.y]){
dist[0][t.y] = dist[t.x][t.y] + 1;
st[0][t.y] = true;
q[++ tt] = {0, t.y};
pre[0][t.y] = {t.x,t.y,5};
}
int pa = t.x, pb = t.y;
int cntb = b - pb, cnta = a - pa; // cnta:a还能装多少, cntb:b还能装多少
// a 给 b
if(pa >= cntb) {pa -= cntb; pb = b;}
else {pb += pa; pa = 0;}
if(! st[pa][pb]) {
dist[pa][pb] = dist[t.x][t.y] + 1;
st[pa][pb] = true;
q[++ tt] = {pa, pb};
pre[pa][pb] = {t.x,t.y,3};
}
pa = t.x,pb = t.y;
cntb = b - pb, cnta = a - pa;
if(pb >= cnta) {pb -= cnta; pa = a;}
else {pa += pb; pb = 0;}
if(! st[pa][pb]) {
dist[pa][pb] = dist[t.x][t.y] + 1;
st[pa][pb] = true;
q[++ tt] = {pa,pb};
pre[pa][pb] = {t.x,t.y,4};
}
}
return INF;
}
int main()
{
init();
scanf("%d%d%d",&a,&b,&c);
int t = bfs();
if(t == INF)
{
printf("impossible\n");
}
else
{
printf("%d\n",t);
}
stack<string> stk;
while(prea != 0 || preb != 0)
{
stk.push(result[pre[prea][preb].t]);
int tmp = prea;
prea = pre[prea][preb].x;
preb = pre[tmp][preb].y;
}
while(stk.size())
{
cout << stk.top() <<endl;
stk.pop();
}
return 0;
}

浙公网安备 33010602011771号