[kuangbin带你飞]专题一 简单搜索 题解

时间:2020-05-20
本文章向大家介绍[kuangbin带你飞]专题一 简单搜索 题解,主要包括[kuangbin带你飞]专题一 简单搜索 题解使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

A - 棋盘问题 POJ 1321

一个不规则的棋盘,‘#’区域才可以摆放旗子,且同行同列只能有一个棋子。输出k个棋子的所有摆放方案。

思路:

​遍历每一行每一列,放棋子加标记dfs即可。

int n,k,ans;
string s[10];
bool column[10];
void dfs(int r,int k){//k 当前剩余棋子数 
	if(k == 0){
		++ans;
		return;
	}
	if(r == n) return;
	dfs(r + 1,k);
	for(int j = 0; j < n; j++){
		if(s[r][j] == '#' && !column[j]){
			column[j] = true;
			dfs(r + 1, k - 1);
			column[j] = false;//不放
		}
	}
}
int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);
	while(cin >> n >> k){
		if(n == -1 && k == -1) break;
		for(int i = 0; i < 8; i++) column[i] = false;
		ans = 0; 
		memset(column,false,sizeof column);
		for(int i = 0; i < n; i++) cin >> s[i];
		dfs(0,k);
		cout << ans << endl;
	}
	return 0;
}
B - Dungeon Master POJ 2251

给你一个3维的迷宫,'#'为岩石不能通过,'.'可以通过。起点'S',终点'E'。每走一步耗时为1,询问到达重点的最短时间。直接bfs即可,字符数组g(z,x,y):第z层,x行,y列。在每一个位置可以前后左右上下移动。

string s[35][35];
bool flag,vis[31][31][31];
struct node{
	int z,x,y,time;
}cur,top;
int a,b,c,sx,sy,sz,dir[6][3] = {1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1};
bool check(node cur){
	if(vis[cur.z][cur.x][cur.y]) return false;
	if(cur.z < 0 || cur.x < 0 || cur.y < 0 || cur.z >= c || cur.x >= a || cur.y >= b) return false;
	return true;
}
void bfs(){
	cur.z = sz,cur.x = sx,cur.y = sy,cur.time = 0;
	queue<node> q;
	q.push(cur);
	while(!q.empty()){
		top = q.front();
		q.pop();
		cur.time = top.time + 1;
		for(int i = 0; i < 6; i++){
			cur.z = top.z + dir[i][0];
			cur.x = top.x + dir[i][1];
			cur.y = top.y + dir[i][2];
			if(check(cur) && s[cur.z][cur.x][cur.y] != '#'){
				if(s[cur.z][cur.x][cur.y] == 'E'){
					cout << "Escaped in " << cur.time << " minute(s)." << endl;
					return;
				}
				vis[cur.z][cur.x][cur.y] = true;
				q.push(cur);
			}
		}
	}
	cout << "Trapped!" << endl; 
}
int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	while(cin >> c >> a >> b && (a + b + c)){
		flag = true;
		memset(vis,false,sizeof vis);
		for(int i = 0; i < c; i++){
			for(int j = 0; j < a; j++){
				cin >> s[i][j];
				for(int k = 0; k < b; k++){
					if(s[i][j][k] == 'S') sz = i,sx = j,sy = k;
				}
			}
		}
		bfs();
	}
	return 0;
}
C - Catch That Cow POJ 3278

John初始位于n位置,牛位于k位置。牛的位置不变,john有两种移动方式:

  1. 移动到相邻位置 x + 1 or - 1
  2. 移动到2 * x位置

找到牛需要的最少移动次数?若牛的位置在人之前,则answer = n - k。其他情况,直接bfs,到达每一个节点,先判断节点是否在[0,100000]中,再判断是否到达过。

  • DP做法:
    • dp(i)表示n移动到n~k间第i个位置所需的最小移动次数
    • dp(i) = abs(i - n),(n >= k,不需要计算)
    • dp[i] 可以通过dp[i - 1],dp[i + 1]得到
    • 第二种移动为2 * i,i 奇偶分开讨论
    • i is odd:dp[i] = min(min(dp[i],dp[i - 1] + 1),min(dp[i - 1 >> 1] + 2,dp[i + 1 >> 1] + 2))
    • i is even:dp[i] = min(min(dp[i],dp[i -1] + 1),min(dp[i >> 1] + 1,dp[i >> 1 + 1] + 2))
int DP(){
	for(int i = 0; i <= k; i++) dp[i] = abs(n - i);//n点一步步移动到i 
	for(int i = n + 1; i <= k; i++){
		dp[i] = min(dp[i],dp[i - 1] + 1);
		if(i & 1){
			dp[i] = min(dp[i],dp[i / 2] + 2);
			dp[i] = min(dp[i],dp[i / 2 + 1] + 2);
		}else{
			dp[i] = min(dp[i],dp[i / 2] + 1);
			dp[i] = min(dp[i],dp[i / 2 + 1] + 2);			
		}
	} 
	return dp[k];
}
D - Fliptile

类似于关灯游戏,对第一行的每一个点,都有关 or 开两种选择,首先枚举第一行的各个状态,之后搜索其他行:上一行中开灯位置下一行必须翻转 -- 所有行翻转完毕后,判断最后一行是否全零。字典序最小,左上-- 右下枚举,靠近右下最优。

const int kN = 17,inf = 0x3f3f3f3f;
int n,m,res,g[kN][kN],book[kN][kN],mid[kN][kN],ans[kN][kN],dir[2][5] = {1,-1,0,0,0,0,0,1,-1,0};
//g数组,记录当前的各点状态,book,存储各点初始状态。
//mid数组,记录当前各点翻转情况,ans数组,存储当前最优翻转情况
bool check(int x,int y){
	if(x < 0 || y < 0 || x >= n || y >= m) return false;
	return true;
}
void turn(int x,int y){
	for(int i = 0; i < 5; i++){
		int tx = x + dir[0][i];
		int ty = y + dir[1][i];
		if(check(tx,ty)) g[tx][ty] ^= 1;
	}
}
void dfs_b(int r,int sum){//其它行
	if(r == n){//所有行已翻转完毕
		for(int i = 0; i < m; i++){
			if(g[r - 1][i]) return;
		}
		if(sum <= res){
			res = sum;
			memcpy(ans,mid,sizeof mid);
		}
		return;
	}else{
		for(int i = 0; i < m; i++){
			if(g[r - 1][i]){//上一行决定翻转位置 
				++sum;
				mid[r][i] = 1;
				turn(r,i);
			}else mid[r][i] = 0;
		}
		dfs_b(r + 1,sum);//下一行 
	}
} 
void dfs_a(int l,int sum){//列 翻转个数 
	if(l == m){
		for(int i = 0; i < m; i++)
			if(mid[0][i]) turn(0,i);
		dfs_b(1,sum);//其他行 
		memcpy(g,book,sizeof book);//恢复g数组 
		return; 
	}else{
		mid[0][l] = 1;//turn
		dfs_a(l + 1,sum + 1);
		mid[0][l] = 0;//unturn
		dfs_a(l + 1,sum);
	}
}
int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> n >> m;
	for(int i = 0; i < n; i++){
		for(int j = 0; j < m; j++) cin >> book[i][j];
	}
	res = inf;
	memcpy(g,book,sizeof book);
	dfs_a(0,0);
	if(res == inf) cout << "IMPOSSIBLE" << endl;
	else{
		for(int i = 0; i < n; i++){
			for(int j = 0; j < m; j++){
				if(j) cout << ' ';
				cout << ans[i][j];
			}
			cout << endl;
		}
	} 
	return 0;
} 
E - Find The Multiple

输入一个n,输出由0,1组成的m为n的倍数,直接遍历所有可能...

long long bfs(){
	queue<long long> q;
	q.push(1);
	while(!q.empty()){
		long long t = q.front();
		q.pop();
		if(!(t % n)) return t;
		q.push(t * 10);
		q.push(t * 10 + 1);
	}
}
F - Prime Path

找到任意两素数a,b间的最便宜的素数路径:a -->b,每次只能够变换某一位上的数字,且变换的数字也是素数,求最小变换次数。

  • 先使用素数筛求出所有的四位素数
  • bfs,模拟修改。
int t,a,b;
bool prime[10005],vis[10005];
struct node{
	int x,t;
}top;
void init(){//将10000以内的素数标记出来 false 表示是素数 
	prime[1] = true;
	for(int i = 2; i <= 10000; i++){
		if(!prime[i]){
			for(int j = 2 * i; j <= 10000; j += i) prime[j] = true;
		}
	}
}
void bfs(){
	queue<node> q;
	q.push({a,0});
	vis[a] = true;
	while(!q.empty()){
		top = q.front();
		q.pop();
		if(top.x == b){
			cout << top.t << endl;
			return;
		}
		for(int i = 0; i < 4; i++){
			for(int j = 0; j < 10; j++){
				if(i == 3 && !j) continue;//最高位不可为0
				int t =  top.x - (top.x / int(pow(10,i)) % 10) * pow(10,i) + j * pow(10,i);
				if(!prime[t] && !vis[t]){
					vis[t] = true;
					q.push({t,top.t + 1});
				}
			}
		}
	}
	cout << "Impossible" << endl;
}
int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> t;
	init(); 
	while(t--){
		memset(vis,false,sizseof(vis));
		cin >> a >> b;
		bfs();
	}
	return 0;
} 
G - Shuffle'm Up

两个字符串s1,s2,均含有c个字符。s1,s2交叉组成s12,s12尾部的c个字符为s1,其它为s2,循环操作。输入s1,s2,最终状态s12。询问结果多少次操作可以得到s12。。。

  • 模拟......,记录每次模拟的s12,注意不要重复操作
H - Pots

两个瓶子,量出 c L水需要的最少操作数。

操作:

1. fill(i),倒满i
   2. drop(i),全部倒掉i中的水
   3. pour(i,j),将i中的水倒入j中

思路:

1.两个杯子之间互倒(两种)

2.每个杯子可以倒满后者全部倒掉

3.由于需要输出最终操作步骤,在结构体中增加一个数组存储每次的操作

4.跑一边bfs...

//代码大致思路同M - 非常可乐
node.turn = top.turn+1;
if(top.a < a){//fill 
	node.a = a,node.b = top.b;
	if(!vis[node.a][node.b]){
		node.ans = top.ans;
		node.ans.push_back("FILL(1)");
		q.push(node);
		vis[node.a][node.b] = true;
	}
}	
I - Fire Game

输入n*m的矩阵,'#'表示草地,可燃烧。'.'表示空地,不可燃烧.询问点燃哪两块草地,可以是所有的草地最快燃烧。

  • 对任意一块草地,bfs,记录到达各点的时间,记录最后一层燃烧的任意一个草地x
    • 若bfs结束后存在未燃烧草地,则直接计算未燃烧草地燃烧用时
  • x bfs,记录时间,当到达某点时用时更小则修改这个点,维护一个最大时间。
J - Fire!

J点位人所在初始位置,F点位各个着火点,每个着火点bfs,记录其他可燃烧点的最快燃烧时间,最后j点bfs,判断是否存在到达边界时未燃烧的点。

K - 迷宫问题

定义一个5X5的二维数组:它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。

要求输出左上角到右下角的最短路径。

  • bfs并记录每一步的上一个位置(模拟链表思想),最后通过栈输出路径...
//bfs模板题 记录各自父节点 找到答案 父节点入栈 --> 输出 
int g[6][6],dir[2][4] = {-1,1,0,0,0,0,-1,1};
bool vis[6][6];
struct node{
	int x,y;
}cur,top,record[6][6];
bool judge(int x,int y){
	if(g[x][y] || x < 0 || x >= 5 || y < 0 || y >= 5) return false; 
	return true;
}
void bfs(){
	queue<node> q;
	q.push({0,0});
	vis[0][0] = true;
	while(!q.empty()){
		top = q.front();
		q.pop();
		if(top.x == 4 && top.y == 4){//输出结果 
			stack<node> s;
			cur = top;
			while(cur.x || cur.y){
				s.push(cur);
				cur = record[cur.x][cur.y];
			}
			cout << "(0, 0)" << endl;
			while(!s.empty()){
				cur = s.top();
				s.pop();
				cout << '(' << cur.x << ", " << cur.y << ')' << endl;
			}
			return;
		}
		for(int i = 0; i < 4; i++){
			cur.x = top.x + dir[0][i];
			cur.y = top.y + dir[1][i];
			if(judge(cur.x,cur.y) && !vis[cur.x][cur.y]){
				vis[cur.x][cur.y] = true;
				q.push(cur);
				record[cur.x][cur.y] = top;
			}
		} 
	}
}
int main(void){
	ios_base::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	for(int i = 0; i < 5; i++){
		for(int j = 0; j < 5; j++) cin >> g[i][j];
	}
	bfs();
	return 0;
} 
L - Oil Deposits

'@ 表示石油,每找到一个@ - bfs标记相连的@,计算bfs次数。

int ans,n,m,dir[3] = {0,-1,1};
string g[105];
bool vis[105][105];
struct node{
	int x,y;
}cur,top;
bool judge(node cur){
	if(cur.x < 0 || cur.y < 0 || cur.x >= n || cur.y >= m) return false;
	if(g[cur.x][cur.y] == '*') return false;
	return true;
}
void bfs(int x,int y){
	queue<node> q;
	q.push({x,y});
	vis[x][y] = true;
	while(!q.empty()){
		top = q.front();
		q.pop();
		for(int i = 0; i < 3; i++){
			for(int j = 0; j < 3; j++){
				cur.x = top.x + dir[i];
				cur.y = top.y + dir[j];
				if(judge(cur) && !vis[cur.x][cur.y] && (i | j)){
					vis[cur.x][cur.y] = true;
					q.push(cur);
				}
			}
		}
	}
}
int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	while(cin >> n >> m){
		if(!n && !m) break;
		memset(vis, false, sizeof vis);
		ans = 0;
		for(int i = 0; i < n; i++) cin >> g[i];
		for(int i = 0; i < n; i++){
			for(int j = 0; j < m; j++){
				if(g[i][j] == '@' && !vis[i][j]){
					bfs(i,j);
					++ans;
				}
			}
		}
		cout << ans << endl;
	}
	return 0;
}
M - 非常可乐

大家一定觉的运动以后喝可乐是一件很惬意的事情,但是seeyou却不这么认为。因为每次当seeyou买了可乐以后,阿牛就要求和seeyou一起分享这一瓶可乐,而且一定要喝的和seeyou一样多。但seeyou的手中只有两个杯子,它们的容量分别是N 毫升和M 毫升 可乐的体积为S (S<101)毫升 (正好装满一瓶) ,它们三个之间可以相互倒可乐 (都是没有刻度的,且 S==N+M,101>S>0,N>0,M>0) 。聪明的ACMER你们说他们能平分吗?如果能请输出倒可乐的最少的次数,如果不能输出"NO"。

  • s需要均分,必须为偶数

  • 3个杯子互相倒,每种状态有六种可能的倒法:n->m n->s m->n m->s s->n s->m,bfs。。。

//s->n n未满 s有剩余 
if(top.s && top.n<n){
	//s剩余量可不可以使n填满 
	if(top.s >= n-top.n) node.n = n,node.s = top.s-n+top.n;
	else node.n = top.n+top.s,node.s = 0; 
	node.m = top.m;
	node.time = top.time+1;
	//每种状态入队一次 
	if(!vis[node.n][node.m]){ 
		q.push(node);
		vis[node.n][node.m] = true;
	} 
}
  • 数学做法:...
N - Find a way

Y和M要在‘@’见面,每走一步耗时十一分钟,初始时y m位于两个位置。两人要去的‘@’总耗时最小。

  • ‘@’有多个,对每一个跑一遍bfs()求最小总步数会超时....

  • y,m各自bfs(),记录到达各个‘@’的时间,寻找到达‘@’的步数总和最小的点...

const int INF = 0x3f3f3f3f;
int n,m,ans,book[2][210][210],dir[4][2] = {{-1,0},{1,0},{0,-1},{0,1}};
struct node{
	int x,y,step;
}cur,top;
vector<pair<int,int>> posi;
string g[205];
bool vis[205][205]; 
bool judge(int x, int y){
	if(x < 0 || x >= n || y < 0 || y >= m) return false;
	if(g[x][y] == '#') return false;
	return true;
}
void bfs(int f, int x, int y){
	memset(vis,false,sizeof(vis));
	vis[x][y] = true;
	queue<node> q;
	q.push({x,y,0});
	while(!q.empty()){
		top = q.front();
		q.pop();
		cur.step = top.step+1;
		if(g[top.x][top.y] == '@'){//'@'所在位置记录一遍 
			book[f][top.x][top.y] = top.step;
			if(f) posi.push_back(make_pair(top.x,top.y));	
		}
		for(int i = 0; i < 4; i++){
			cur.x = top.x + dir[i][0];
			cur.y = top.y + dir[i][1];
			if(judge(cur.x, cur.y) && !vis[cur.x][cur.y]){
				vis[cur.x][cur.y] = true;
				q.push(cur);
			}
		}
	} 
}

int main(void){
	ios_base::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	while(cin >> n >> m){
		for(int i=0;i<n;i++) cin >> g[i];
		for(int i = 0; i < n; i++){
			for(int j = 0; j < m; j++){
				if(g[i][j] == 'Y') bfs(1,i,j); 
				if(g[i][j] == 'M') bfs(0,i,j);
			}
		}
		ans = INF;
 		for(int i = 0; i < posi.size(); i++){//最小总步数
			ans = min(ans,book[0][posi[i].first][posi[i].second] + book[1][posi[i].first][posi[i].second]);
		} 
		posi.clear();
		cout << ans * 11 << endl;
	}
	return 0; 
}

原文地址:https://www.cnblogs.com/honey-cat/p/12924720.html