搜索训练笔记
BFS(广度优先搜索)
Catch The Cow
- 题目大意
在一坐标轴上给定起点和终点,每次行动可以向左、向右或将目前的坐标变为两倍,问走到终点的最少步数。
$ n \leq 100000 , k \leq 100000$
- 思路
注意到题目中的“最少”,考虑使用bfs来处理即可。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
struct node {
int pos;
int time;
};
int vis[100010];
int n,m;
int bfs(int pos) {
queue <node> q;
node a;
a.pos = pos;
a.time = 0;
q.push(a);
node nxt;
while(!q.empty()) {
node u = q.front();
q.pop();
if(u.pos == m) {
return u.time;
}
nxt = u;
nxt.pos = u.pos + 1;
if(!(nxt.pos < 0 or nxt.pos > 100000 or vis[nxt.pos])) {
nxt.time = u.time + 1;
vis[nxt.pos] = 1;
q.push(nxt);
}
nxt.pos = u.pos - 1;
if(!(nxt.pos < 0 or nxt.pos > 100000 or vis[nxt.pos])) {
nxt.time = u.time + 1;
vis[nxt.pos] = 1;
q.push(nxt);
}
nxt.pos = u.pos << 1;
if(!(nxt.pos < 0 or nxt.pos > 100000 or vis[nxt.pos])) {
nxt.time = u.time + 1;
vis[nxt.pos] = 1;
q.push(nxt);
}
}
}
int main () {
cin >> n >> m;
cout << bfs(n) << endl;
return 0;
}
Find The Multiple
- 题目大意
找一个数m的非零倍数,这个非零倍数只由0,1组成,\(m \leq 200\)
- 思路
注意到直接搜的话,容易TLE,直接打表(
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
#define int long long
int lis[] = {0,1,10,111,100,10,1110,1001,1000,111111111,10,11,11100,1001,10010,1110,10000,11101,1111111110,11001,100,10101,110,110101,111000,100,10010,1101111111,100100,1101101,1110,111011,100000,111111,111010,10010,11111111100,111,110010,10101,1000,11111,101010,1101101,1100,1111111110,1101010,10011,1110000,1100001,100,100011,100100,100011,11011111110,110,1001000,11001,11011010,11011111,11100,100101,1110110,1111011111,1000000,10010,1111110,1101011,1110100,10000101,10010,10011,111111111000,10001,1110,11100,1100100,1001,101010,10010011,10000,1111111101,111110,101011,1010100,111010,11011010,11010111,11000,11010101,1111111110,1001,11010100,10000011,100110,110010,11100000,11100001,11000010,111111111111111111,100,101,1000110,11100001,1001000,101010,1000110,100010011,110111111100,1001010111,110,111,10010000,1011011,110010,1101010,110110100,10101111111,110111110,100111011,111000,11011,1001010,10001100111,11101100,1000,11110111110,11010011,10000000,100100001,10010,101001,11111100,11101111,11010110,11011111110,11101000,10001,100001010,110110101,100100,10011,100110,1001,1111111110000,11011010,100010,1100001,11100,110111,11100,1110001,11001000,10111110111,10010,1110110,1010100,10101101011,100100110,100011,100000,11101111,11111111010,1010111,1111100,1111110,1010110,11111011,10101000,10111101,111010,1111011111,110110100,1011001101,110101110,100100,110000,100101111,110101010,11010111,11111111100,1001111,10010,100101,110101000,1110,100000110,1001011,1001100,1010111010111,110010,11101111,111000000,11001,111000010,101010,110000100,1101000101,1111111111111111110,111000011,1000};
//int bfs(int now) {
// queue <int> q;
// q.push(1);
// while(!q.empty()) {
// int u = q.front();
// q.pop();
// if(u % now == 0) {
// return u;
// }
// q.push(u * 10);
// q.push(u * 10 + 1);
// }
//}
int m;
signed main () {
//freopen("s.txt","w",stdout);
while(~scanf("%lld",&m) and m) {
cout << lis[m] << endl;
}
return 0;
}
Prime Path
- 题目大意
给两个素数,问能否通过变化每一位上的数字,使得较小的素数变为较大的,并且变化过程所拓展的数也应是素数,多组数据
- 思路
每一次改变每一位的数值,从个十百千递增修改,保证严格递增修改,之后bfs即可。
ps:不知道为什么我在poj上提交的时候会有sqrt重定义错误的问题,改为g++就没问题了。。。
#include <iostream>
#include <cstdio>
#include <queue>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
struct node {
int num;
int stp;
};
int n;
int vis[10010];
bool isprime(int n) {
if(n == 0 or n == 1) return 0;
else if(n == 2 or n == 3) return 1;
else {
for(int i = 2;i <= int(sqrt(n)); ++i) {
if(n % i == 0) return 0;
}
return 1;
}
}
int bfs(int st,int ed) {
if(st == ed) return 0;
memset(vis,0,sizeof vis);
queue <node> q;
node tmp;
tmp.num = st;
tmp.stp = 0;
q.push(tmp);
vis[tmp.num] = 1;
while(!q.empty()) {
node u = q.front();
q.pop();
for(int i = 1;i <= 9; i += 2) {
int now = u.num / 10 * 10 + i;
if(now == ed) {
return u.stp + 1;
}
if(!vis[now] and isprime(now)) {
vis[now] = 1;
node ss;
ss.num = now;
ss.stp = u.stp + 1;
q.push(ss);
}
}
for(int i = 0;i <= 9; ++i) {
int now = u.num / 100 * 100 + i * 10 + u.num % 10;
if(now == ed) {
return u.stp + 1;
}
if(!vis[now] and isprime(now)) {
vis[now] = 1;
node ss;
ss.num = now;
ss.stp = u.stp + 1;
q.push(ss);
}
}
for(int i = 0;i <= 9; ++i) {
int now = u.num / 1000 * 1000 + i * 100 + u.num % 100;
if(now == ed) {
return u.stp + 1;
}
if(!vis[now] and isprime(now)) {
vis[now] = 1;
node ss;
ss.num = now;
ss.stp = u.stp + 1;
q.push(ss);
}
}
for(int i = 1;i <= 9; ++i) {
int now = i * 1000 + u.num % 1000;
if(now == ed) {
return u.stp + 1;
}
if(!vis[now] and isprime(now)) {
vis[now] = 1;
node ss;
ss.num = now;
ss.stp = u.stp + 1;
q.push(ss);
}
}
}
return -1;
}
int main () {
cin >> n;
for(int i = 1;i <= n; ++i) {
int x,y;
cin >> x >> y;
int tmp = bfs(x,y);
if(tmp == -1) {
puts("Impossible");
}
else {
cout << tmp << endl;
}
}
return 0;
}
Pots
- 题目大意
给两个有容积的盆,和一个需要凑出来的水升数,你只能加满水,倒水,或将一个盆中水导入另外一个,至少经过几步能完成,并输出路径
- 思路
计\(vis_{i,j}\)表示左右盆中水盛情况是否达到过,每次模拟一个操作,记录并搜索即可,代码后补