搜索训练笔记

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}\)表示左右盆中水盛情况是否达到过,每次模拟一个操作,记录并搜索即可,代码后补
原文地址:https://www.cnblogs.com/akoasm/p/14635300.html