bfs题解

Vjudge

A - Catch That Cow

John和奶牛分别在数轴的n和k上,john去找奶牛且奶牛不会动
john有三种移动的方式
1.向前一格
2.向后一格
3.从点x移动到点2*x
求john到奶牛的位置最少需要几步
对于n>k的情况,John只能一步步往后退去找牛,所以步数为n-k
对于n==k的情况,John不用动,这种情况可以和第一种情况合并
对于n<k的情况,通过bfs去搜索最少需要几步

#include<iostream>
#include<queue>
using namespace std;

const int N=1e5+5;
int n,k;
int vis[N];   //标记是否走过该点 
int step[N];  //记录步数 

bool check(int x){   //判断这个点是否在范围内 
	if(x>=0 && x<=1e5 ) 
		return true;
	return false;
}

int bfs(){
	queue <int> q;
	int start,next;
	step[n]=0;//在n点的时候步数为0 
	q.push(n);
	vis[n]=1;//标记n点走过 
	while(!q.empty()){
		start = q.front();
		if(start==k){   //  到达牛的位置  退出 
			return step[start];
		}
		q.pop();//记得取出后删除 
		for(int i=0;i<3;i++){  //三种走法 
			if(i==0){//第一种走法,向前一格 
				next=start+1;
			}
			if(i==1){//第二种走法,向后一格 
				next=start-1;
			}
			if(i==2){//第三种走法,从x移到2*x 
				next=start*2;
			}
			if(check(next) && vis[next]==0){//判断在范围内且没有走过 
				q.push(next);
				step[next]=step[start]+1;  //步骤+1 
				vis[next]=1;//标记走过 
			}
		}
	}
	return -1;
}

int main(){
	cin >> n >> k; 
	if(n>=k){   //这种情况下只能一步步后退 
		cout <<n-k << endl;
	} 
	else{
		cout<<bfs()<<endl;
	}
	return 0;
}

B - Find The Multiple

给一个n,要求求出一个由0和1构成的数字,使得n能被这个数整除
如果给定n有多个解,其中任何一个都是可以接受的。
从1 开始,在后面依次添加0或者1,即x -> x*10x -> x*10+1
当找到一个数能够整除n时,就结束搜索
要注意的是,这题要用ll

#include<iostream>
#include<queue>
typedef long long ll;
using namespace std;

int n;

void bfs(int n){
	queue<ll>q;//注意要用ll 
	q.push(1);
	while(!q.empty()){
		ll x=q.front(); 
		q.pop();
		if(x%n==0){//找到一个数能够整除n 
			cout << x << endl;//输出这个数
			return; 
		}
		q.push(x*10);//添加0 
		q.push(x*10+1);//添加1 
	} 
}

int main(){
	while(cin >> n && n){
	 	bfs(n);
	}
	return 0;
}

C - Prime Path

给你t组数据
每组数据给你两个四位数,每次操作只能修改一位数字
要求每次修改后的四位数都是质数
问最少花费(也就是操作次数)几次可以将第一个四位数修改成为第二个四位数
如何修改数字呢?每次将数拆成个十百千四位
改变其中一个数字,在将这四个数字组成四位数
通过判断这个数是不是四位数,并且是不是素数来剪枝
如何判断其是否为素数?埃氏筛

#include<iostream>
#include<queue>
#include<string.h> 
using namespace std;

const int N=1e4+5;
int n,m;
int vis[N];
int prime[N];//素数表 

struct node{
	int x;//四位数的值 
	int cnt;//当前四位数所用的花费 
};

void isprime(){
	memset(prime,0,sizeof(prime)); // 初始化每一个都是素数 
	prime[0]=1;//不是素数 
	prime[1]=1;//不是素数 
	for(int i=2;i<N;i++){  // 埃氏筛法判断素数 
		if(!prime[i]){
			for(int j=2*i;j<N;j=j+i){
				prime[j]=1;  //不是素数 
			}
		}
	} 
}

int check(int x){//判断是否是四位数并且是素数 
	if(!prime[x] && x>=1000 && x<=9999){
		return 1;
	}
	else
		return 0;
} 

void bfs()
{
	node st,next;
	queue<node>q;
	st.x=n;
	st.cnt=0;//初始花费为0 
	vis[st.x]=1;//标记这个数字已经出现 
	q.push(st);
	int a[4];//用来存四位数 
	while(!q.empty()){
		st=q.front();
		q.pop();
		a[0]=st.x%10;//个位
		a[1]=st.x/10%10;//十位 
		a[2]=st.x/100%10;//百位 
		a[3]=st.x/1000%10; //千位
		for(int i=0;i<4;i++){//对每一个数字进行改变 
			int temp=a[i];//标记用于回溯 
			for(int j=0;j<=9;j++){//数字可以是0~9 
				if(a[i]!=j){
					a[i]=j;
					next.x=a[3]*1000+a[2]*100+a[1]*10+a[0]; // 新的数字 
					if(next.x==m){  //与m相等,输出答案结束循环 
						cout << st.cnt+1 << endl;//输出花费,也就是最少操作数 
						return ;
					}
					if(check(next.x) && !vis[next.x]){ //判断合法 
						next.cnt=st.cnt+1;//花费增加 
						vis[next.x]=1;//标记已经出现 
						q.push(next);
					}
				}
			}
			a[i]=temp; // 回溯 
		} 
	}
	cout << "Impossible" << endl;//无法修改成为目标四位数 
	return;
}

int main()
{
	int t;
	cin>>t;
	isprime();//埃氏筛打表 
	while(t--){
		memset(vis,0,sizeof(vis));//初始化 
		cin>>n>>m;
		if(n==m){//输入的时候两个数就是相等的 
			cout << 0 << endl;
		}
		else
			bfs();
	}
	return 0;
}

D - Pots

有两杯水,容量分别为a,b
你可以进行三种操作
1.FILL(i) 装满水
2.DROP(i) 水全部倒掉
3.POUR(i,j) i中的水倒入j
通过最少的操作次数,使得一个罐子里恰好有c升水
同时要输出操作方法
根据题意,可以知道一共有六种操作:
FILL(1) FILL(2) DROP(1) DROP(2) POUR(1,2) POUR(2,1)
要注意的是要在结构体中开一个数组用来记录操作

#include<iostream>
#include<queue>
using namespace std;

const int N=105; 
int a,b,c;
bool vis[N][N];
string path[] = {"FILL(1)","FILL(2)","DROP(1)","DROP(2)","POUR(1,2)","POUR(2,1)"};//六种操作 

struct node {
   int a, b;//两个水杯中的水的多少 
   int path[N];//存储路径
   int step;//操作步数 
};

void pr(int step,int p[]){//输出 
   cout << step << endl;//输出操作数 
   for(int i=0; i<step; i++)
       cout << path[p[i]] << endl;//输出每一步的操作 
}

void bfs(){
   queue<node> q; 
   node st;
   st.a=0;//a水杯初始没有水 
   st.b=0;//b水杯初始没有水 
   st.step=0;//输出操作数为0 
   q.push(st);
   vis[st.a][st.b] = 1;//标记这种情况已经出现 
   while(!q.empty()) {
       st = q.front();
       q.pop();
       if(st.a==c||st.b==c) {//两杯水中其中一杯水为c 
           pr(st.step, st.path);//输出操作方法 
           return;
       }
   	node next;
       next = st;
       next.step++;//操作数+1 
       // FILL(a)
       if(a-st.a>0) {//当前a水杯未满 
           next.a = a;
           next.b = st.b;
           if(!vis[next.a][next.b]) {
               next.path[st.step] = 0;//记录路径 
               q.push(next);
               vis[next.a][next.b]=1;
           }
       }
       // FILL(b)
       if(b - st.b > 0) {//当前b水杯未满 
           next.a = st.a;
           next.b = b;
           if(!vis[next.a][next.b]) {
               next.path[st.step] = 1;
               q.push(next);
               vis[next.a][next.b]=1;
           }
       }
       // DROP(a)
       if(st.a) {//a水杯有水 
           next.a = 0;
           next.b = st.b;
           if(!vis[next.a][next.b]) {
               next.path[st.step] = 2;
               q.push(next);
               vis[next.a][next.b]=1;
           }
       }
       // DROP(b)
       if(st.b) {//b水杯有水 
           next.a = st.a;
           next.b = 0;
           if(!vis[next.a][next.b]) {
               next.path[st.step] = 3;
               q.push(next);
               vis[next.a][next.b]=1;
           }
       }
       // POUR(a,b)
       if(st.a && (st.b < b)) {//a水杯有水并且b水杯未满 
           if(st.a > (b - st.b)) {//倒完以后a水杯还有剩余的水,b水杯装满 
               next.a = st.a -(b - st.b);
               next.b = b;
           } 
   		else {//a水杯的所有水都倒入b 
               next.a = 0;
               next.b = st.b + st.a;
           }
           if(!vis[next.a][next.b]) {
               next.path[st.step] = 4;
               q.push(next);
               vis[next.a][next.b]=1;
           }
       }
       // POUR(b,a)
       //和上一操作同理,只是a,b互换 
       if(st.b && (st.a < a)) {
           if(st.b > (a - st.a)) {
               next.a = a;
               next.b = st.b -(a - st.a);
           } 
   		else {
               next.a = st.a + st.b;
               next.b = 0;
           }
           if(!vis[next.a][next.b]) {
               next.path[st.step] = 5;
               q.push(next);
               vis[next.a][next.b]=1;
           }
       }
   }
   cout << "impossible" << endl;//所有情况都搜索完,还是没有找到目标情况,输出不可能 
   return;
}

int main()
{
   cin >> a >> b >> c;
   bfs();
   return 0;
}

E - Asteroids!

给出一个n*n*n的三维立体
每次输入一层
给出两个点(x,y,z),
求出从初始点到目标点的最短路径
只能走O但不能走X
如果无法到达输出"NO ROUTE"
与之前的题目不同的是,这一题要把二维数组开到三位数组
要注意给出的两点的x,y,z的坐标顺序,读清楚题
输出的是n和步数

#include<iostream>
#include<queue>
using namespace std;

const int N=15; 
int n,x1,y1,z1,x2,y2,z2;
char mp[N][N][N]; 
int vis[N][N][N];//标记点是否出现 
int dirx[6]={1,0,0,-1,0,0};//x方向 
int diry[6]={0,1,0,0,-1,0};//y方向 
int dirz[6]={0,0,1,0,0,-1};//z方向 

struct node{
   int x,y,z;//坐标 
   int step;//步数 
};

bool check(int x,int y,int z){
   if(x>=0&&x<n&&y>=0&&y<n&&z>=0&&z<n){
   	return true;
   }
   return false;
}

void bfs(){
   node st,next;
   queue<node>q;
   st.x=x1;
   st.y=y1;
   st.z=z1;
   st.step=0;
   q.push(st);
   vis[st.x][st.y][st.z]=1;
   if(st.x==x2&&st.y==y2&&st.z==z2){
   	cout << n << " " << st.step << endl;
   	return;
   }
   while(!q.empty()){
   	st=q.front();
   	q.pop();
   	for(int i=0;i<6;i++){
   		next.x=st.x+dirx[i];
   		next.y=st.y+diry[i];
   		next.z=st.z+dirz[i];
   		next.step=st.step+1;//步数+1 
   		if(next.x==x2&&next.y==y2&&next.z==z2){//到达目标点 
   			cout << n << " " << next.step << endl;
   			return;
   		}
   		if(check(next.x,next.y,next.z)&&!vis[next.x][next.y][next.z]&&mp[next.x][next.y][next.z]=='O'){
   			vis[next.x][next.y][next.z]=1;
   			q.push(next);
   		}
   	}
   }
   cout << "NO ROUTE" << endl;
   return;
}

int main()
{
   string s,t;
   while(cin >> s >> n){
   	for(int i=0;i<n;i++){
   		for(int j=0;j<n;j++){
   			for(int k=0;k<n;k++){
   				cin >> mp[i][j][k];//三维立方体 
   			}
   		}
   	}
   	cin >> y1 >> z1 >> x1 >> y2 >> z2 >> x2 >> t;//输入起始点和目标点,别忘了最后有个END要输入 
   	bfs();
   }
   return 0;
}

F - Friend Chains

给出n个人的名字和m对关系
求关系最远的一对的朋友链是多少
例如x和y是朋友,y和z是朋友,x和z没有关系的话,x和z之间的朋友链即为2
用vector存每个人的朋友有哪些
由于给出的是人的名字
为了方便,可以用map来存名字和数字的对应关系,将名字转化成数字
对于每一个人的朋友进行搜索,找到朋友链最长的记录下来
要注意的是一个朋友链可以连接任意2个人,它不超过7个人。

#include<bits/stdc++.h>
using namespace std;

const int N=1e3+5;
vector<int>ve[N];//存第i个人的朋友有哪些 
int vis[N];//记录点是否被记录过 
int ans;//最终答案 
int flag;//标记是否有答案 

void bfs(int x){
	queue<int>q;
	memset(vis,-1,sizeof(vis));//每次搜索的时候都要初始化,所有的点都没有经过 
	q.push(x);//将初始点放入 
	vis[x]=0;//初始点的路径为0 
	while(!q.empty()){//只要是非空,就说明还有路可以走 
		int now=q.front();
		q.pop();//记得删除 
		if(vis[now]>ans){//有了更优的解 
			ans=vis[now];
			if(ans>7){//只有小于等于6,才符合 
				flag=0;
				return;
			} 
		}
		for(int i=0;i<ve[now].size();i++){//对当前搜索的点能够到达的点进行遍历 
			if(vis[ve[now][i]]==-1){//还没有遍历过 
				vis[ve[now][i]]=vis[now]+1;//路径长+1 
				q.push(ve[now][i]);//将这个点存入 
			}
		}
	}
	return;
}

int main()
{
	int n;
	while(cin >> n && n){
		map<string,int>mp;//用来存名字和数字的对应关系,将名字转化成数字 
		for(int i=1;i<=n;i++){
			string s;
			cin >> s;
			mp[s]=i;
		}
		int m;
		cin >> m;
		memset(ve,0,sizeof(ve));//初始化,人与人之间都不是朋友 
		while(m--){
			string x,y;
			cin >> x >> y;
			//相互是朋友 
			ve[mp[x]].push_back(mp[y]);
			ve[mp[y]].push_back(mp[x]);
		}
		flag=1;//初始是有答案的 
		ans=0;//初始答案为0 
		for(int i=1;i<=n;i++){//对于每个人进行搜索 
			bfs(i);
		} 
		if(flag){
			cout << ans << endl;
		}
		else{
			cout << -1 << endl;
		} 
	}
 } 

G - Eight

给出八数码的初始状态,要求输出移动路径,使得最终变成状态12345678空
利用康托值对状态进行判重,回溯输出移动方向

#include<iostream>
#include<queue>
#include<map>
#include<cstdlib>

using namespace std;
const int MAXN=1e6+5;

char op[4]={'u','d','l','r'};//输出的移动方向 
int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};// 四个方向 
int fac[9]={ 1, 1, 2, 6, 24, 120, 720, 5040, 40320};//0! 1! 2! 3!... 
int vis[MAXN];//标记当前康托值所对应的状态是否出现过 
int flag;//标记是否有解决方法 
int idx;//x的初始位置 
char path[MAXN];//记录路径 
int pre[MAXN];//记录上一步的康托值 

struct node{
   int s[9];//状态数组 
   int c;//当前状态的康托值 
   int loc;//x的位置 
};

int cantor(int s[]){//计算康托值 
   int ans=1;//注意,因为 12345..是算作0开始计算的,最后结果要把12345..看作是第一个 
   for(int i=0;i<9;i++){
   	int tmp=0;//用来计数 
   	for(int j=i+1;j<9;j++){
   		if(s[i]>s[j]) tmp++;计算s[i]是第几大的数,或者说计算有几个比他小的数
   	}
   	ans=ans+tmp*fac[9-i-1]; 
   }
   return ans;
}

bool check(int x,int y){//判断是否在3*3的矩阵坐标内 
   if(x>=0 && x<=2 && y>=0 && y<=2){
   	return 1;
   }
   return 0;
}

void bfs(node start){
   queue<node>q;
   node next;
   vis[start.c]=1;//记录开始状态出现过 
   pre[start.c]=-1;//开始状态的上一个状态为-1,用于输出路径 
   q.push(start);//入队 
   while(!q.empty()){
   	start=q.front();//取第一个 
   	q.pop();//取出就删除 
   	if(start.c==1){//如果找到了最终状态123456789,也就是康托值为1 
   		flag=1;//标记有解决方法 
   		return;//结束搜索 
   	}
   	int x=start.loc/3;//x的位置/3即为在3*3矩阵中的x 
   	int y=start.loc%3;//x的位置%3即为在3*3矩阵中的y 
   	for(int i=0;i<4;i++){//四种方向 
   		int nowx=x+dir[i][0];//x改变 
   		int nowy=y+dir[i][1];//y改变 
   		if(check(nowx,nowy)){//判断是否合法 
   			next=start;
   			swap(next.s[nowx*3+nowy],next.s[x*3+y]);//将空格也就是9与移动的那一个数字进行交换位置 
   			next.c=cantor(next.s);//修改康托值 
   			if(!vis[next.c]){//判断这个康托值对应的状态是否出现过 
   				vis[next.c]=1;//标记出现过此种状态 
   				next.loc=nowx*3+nowy;//移动后状态的x的位置 
   				pre[next.c]=start.c;//记录(当前康托值的状态)的(上一步的状态)的康托值 
   				path[next.c]=op[i];//记录得到(当前康托值得状态)的移动方向(包括'u','d','l','r') 
   				q.push(next);//将新得到的状态入队 
   			}
   		}
   	}
   }
   return;
}

void pr(int x){//回溯输出移动方向 
   if(pre[x]==-1){//当这个状态的上一个状态是-1,说明已经到了初始状态 
   	return;
   }
   pr(pre[x]);//查找上一步 
   cout << path[x];//输出移动方向 
}

int main()
{
   ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0); 
   string str;
   int cnt=0;
   node st;//记录初始状态 
   for(int i=0;i<9;i++){
   	cin >> str;//输入起始状态 
   	if(str=="x"){
   		idx=i;//记下x在起始状态中的位置 
   		st.s[cnt++]=9;//起始状态数组 
   	} 
   	else st.s[cnt++]=atoi(str.c_str());//将字符串转为数字 
   }
   st.loc=idx;//x的位置 
   st.c=cantor(st.s);//康托值 
   flag=0;//初始标记无解决方法 
   bfs(st);//广搜 
   if(!flag){//无解决方法 
   	cout << "unsolvable" << endl;
   }
   else{//有解决方法 
   	pr(1);//回溯输出移动方向 
   	cout << endl;
   }
   return 0;
}

wlcam

问题 A: Red and Black

#include<iostream>
#include<queue>// 队列 
using namespace std;

const int N=20+5;// 房间大小范围 
char mp[N][N];// 房间地砖 
int dir[4][2]={{-1,0},{0,-1},{1,0},{0,1}};// 四个方向 
int num;// 所能到达的黑色瓷砖的数量 
int n,m;// 房间的长和宽 

struct node{// 记录瓷砖的坐标 
	int x;
	int y;
};

bool check(int x,int y){// 判断点坐标是否合法 
	if(x>=1 && x<=n && y>=1 && y<=m){// 在整个房间的大小范围内即为合法 
		return 1;
	}
	else{
		return 0;
	}
}

void bfs(int dx,int dy){
	queue<node>q;// 存结构体的队列 
	node start,next;	
	start.x=dx;
	start.y=dy;
	q.push(start);// 起点以结构体的形式进入队列 
	num++;// 起始点也为黑砖 
	while(!q.empty()){// 队列非空 
		start=q.front();// 取出队列第一个元素 
		q.pop();// 取出的同时还要记得删除队列第一个元素 
		for(int i=0;i<4;i++){// 有四个方向 
			next.x=start.x+dir[i][0]; 
			next.y=start.y+dir[i][1];
			//此时next存的是走了一步之后到达的坐标,也就是扩散的点,判断其是否符合条件 
			// check用来判断这个坐标是否合法,同时要判断这个点是否是黑砖 
			if(check(next.x,next.y) && mp[next.x][next.y]=='.'){
				//这个地砖可以到达 
				mp[next.x][next.y]='#';// 将这个地砖变为#,标记这个地砖已经踩过了 
				num++;// 能够到达的黑砖+1 
				q.push(next);// 将符合条件的扩散的点存入队列,用于之后进行扩散 
			}
		}
	}
} 

int main()
{
	int stx,sty;
	while(cin >> m >> n && n && m){// 多组样例输入 且 当n和m为0时结束  
		num=0;// 初始化
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				cin >> mp[i][j];
				if(mp[i][j]=='@'){// 记下起始点的坐标 
					stx=i;
					sty=j;
				}
			}
		}
		bfs(stx,sty);// 广搜 
		cout << num << endl;// 输出能够到达的黑砖的数量 
	}
	return 0;
 } 

问题 B: 细胞有几个

给你一个矩阵,非零的数字代表细胞
如果数字的上下左右还是细胞,则为同一细胞
问有几个细胞
这题也就是问你有几个连通块
可以对每一个非0的细胞的四周进行搜索
如果还是细胞就入队,直到没有连通的细胞了,说明这一片就是一个细胞
注意搜索过的细胞要标记
此处可以将搜索过的细胞的数字标记为0
注意每次读入一位数字可以这样读入scanf("%1d",&a[i][j]);//每次输入一个数

#include<iostream>
#include<stdio.h>
#include<queue>
using namespace std;
const int N=75;
int n,m;
int dir[4][2]={{0,1},{0,-1},{-1,0},{1,0}};
int a[N][N];
int ans;

struct node{
	int x,y;
};

void bfs(int dx,int dy){
	node st;
	queue<node>q;
	st.x=dx;
	st.y=dy;
	a[st.x][st.y]=0;
	q.push(st);
	while(!q.empty()){
		node now=q.front();
		q.pop();
		for(int i=0;i<4;i++){//四个方向 
			node next;
			int nx=now.x+dir[i][0];
			int ny=now.y+dir[i][1];
			if(nx>=0 && ny>=0 && nx<m && ny<n && a[nx][ny]!=0){//没有被标记过并且也是细胞 
				a[nx][ny]=0;//标记搜索到了 
				next.x=nx;
				next.y=ny;
				q.push(next);
			}
		}
	}
	ans++;//这些搜索到的细胞为一个连通块 
}

int main()
{
	cin>>m>>n;
	for(int i=0;i<m;i++){
		for(int j=0;j<n;j++){
			scanf("%1d",&a[i][j]);//每次输入一个数 
		}
	}
	ans=0;
	for(int i=0;i<m;i++){
		for(int j=0;j<n;j++){
			if(a[i][j]!=0){//是细胞 
				bfs(i,j);//从这个细胞开始向四周搜索 
			}
		}
	} 
	cout << ans << endl;
	return 0;
}

问题 C: 面积

计算*围成的0的个数
这题可以把没被包围的地方改成1,这样只需要计算0剩下的个数就好了
从(1,1)点开始搜索,如果附近四个方向中有0的话,就从这个0点继续往下扩散
如果遇到1,就停止,这样能够保证1包围的0一定不会被修改成1

#include<bits/stdc++.h>
using namespace std;
const int N=15;
int a[N][N];
int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
 
struct node{
    int x;
    int y;
};
 
void bfs(){//把没被包围的地方全部改成1
    queue<node>q;
    node start;
    start.x=1;
    start.y=1;
    q.push(start);
    a[1][1]=1;
    while(!q.empty()){
        node now=q.front();
        q.pop();
        for(int i=0;i<4;i++){
            node next;
            next.x=now.x+dir[i][0];
            next.y=now.y+dir[i][1];
            if(a[next.x][next.y]==0&&next.x<=11&&next.x>=0&&next.y>=0&&next.y<=11){
                a[next.x][next.y]=1;
                q.push(next);
            }
        }
    }
}
 
int main(){
    for(int i=1;i<=10;i++){
        for(int j=1;j<=10;j++){
            cin >> a[i][j];
        }
    }
    bfs();
    int sum=0;
    for(int i=1;i<=10;i++){//计算还有多少个0 
        for(int j=1;j<=10;j++){
            if(a[i][j]==0) sum++;
        }
    }
    //剩余的0的个数即为答案 
    cout << sum << endl;
    return 0;
}
/**
如果像以下这种情况,0被分成了好几块
但是由于我们是从左上角开始搜索的,当他遇到1的时候,他就会停止搜索
那么右上方这一片0就不会搜索到,也就不能被修改成1
所以为了能够让这几块0连通,我们可以在整个10*10的方阵最外面添加一圈0
以保证没有被1圈住的0全部被修改成1
0 0 1 0 0 0 0 0 0 1
1 1 1 1 1 1 1 1 1 1
1 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 1
1 1 1 1 1 1 1 1 1 1

56
**/

问题 D: 营救

给出一张海洋图,其中1为陆地,0为海洋,要求从起点到目标点的最短距离
读入的时候注意每次只读一个数字

#include<iostream>
#include<queue>
using namespace std;
const int N=1e3+5;
int n;
int dir[4][2]={{0,1},{0,-1},{-1,0},{1,0}};
char a[N][N];
int vis[N][N];
int sx,sy,ex,ey;
bool check(int x,int y){
	if(!a[x][y] && !vis[x][y] && x>=1 && y>=1 && x<=n && y<=n){
		return true;
	}
	return false;
}

struct node{
	int x;
	int y;
	int step;
};

int bfs(){
	node st;
	st.x=sx;
	st.y=sy;
	st.step=0;
	queue<node>q;
	q.push(st);
	vis[sx][sy]=1;
	while(!q.empty()){
		node now=q.front();
		q.pop();
		for(int i=0;i<4;i++){
			node next;
			next.x=dir[i][0]+now.x;
			next.y=dir[i][1]+now.y;
			next.step=now.step+1;
			if(next.x==ex && next.y==ey){
				return next.step;
			}
			if(check(next.x,next.y)){
				vis[next.x][next.y]=1;
				q.push(next);
			}
		}
	}
	return -1;
}
int main()
{
	cin >> n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			scanf("%1d",&a[i][j]);
		}
	}
	cin >>sx>>sy>>ex>>ey;
	int ans=bfs();
	cout << ans <<endl;
	return 0;
}

问题 E: 最少转弯问题

给出一个矩阵,其中0表示空地,1表示高山,计算从起始点到目标点的最少转弯次数
怎么判断转弯了?每次记录上一步的方向,如果这次移动的方向和上一步的方向相同,则说明没有转弯
如果这次移动的方向和上一步的方向不同,则说明转弯了
最后要输出转弯的最小次数,需要求每次到达目标状态路径的转弯次数的最小值

#include<bits/stdc++.h>
using namespace std;
const int N=105; 
int m,n;
int a[N][N];
int x2,y2;
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};

struct node{
	int x;//坐标 
	int y;//坐标 
	int pre;//上一个方向 
	int cnt;//转弯次数 
};

int bfs(int x,int y){
    queue <node>q;
    node start,now;
    start.x=x;
    start.y=y;
    start.pre=-1;//起始点的上一步为-1 
    start.cnt=0;
    q.push(start);
    int ans=0x3f3f3f3f;//初始化无穷大 
    while(!q.empty()){
        node now=q.front();
        q.pop();
        a[now.x][now.y]=1;//标记已经走过 
        if(now.x==x2&&now.y==y2){//目标状态 
			ans=min(ans,now.cnt);//取最小转弯次数 
			continue;
		}
        for(int i=0;i<4;i++){
            int xx=now.x+dir[i][0];
			int yy=now.y+dir[i][1];
            if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&a[xx][yy]!=1){
                node nn;
                nn.x=xx;
                nn.y=yy;
                if(now.pre==-1){//是第一步 
                    nn.pre=i;
                    nn.cnt=0;
                }
                else if(now.pre!=i){//这一步的方向和上一步的方向不一样 
                    nn.pre=i;//记录方向 
                    nn.cnt=now.cnt+1;//转弯次数+1 
                }
                else{
                    nn.pre=i;//方向相同 
                    nn.cnt=now.cnt;//转弯次数不变 
                }
                q.push(nn);
            }
        }
    }
    return ans;
}
int main(){
    cin >>n >> m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin >> a[i][j];
        }
    }
    int x1,y1; 
    cin >> x1 >> y1 >> x2 >> y2;
    cout << bfs(x1,y1) << endl;
    return 0;
}

问题 F: 马的移动

给出马的起始位置和目标位置,计算最短距离
注意是多组样例输入,每次都要初始化用来标记的数组
注意输出格式

#include <bits/stdc++.h>
using namespace std;

int a[10][10];
int sx,sy,ex,ey;

int dirx[8]={-1,-2,-1,-2,1,2,1,2};
int diry[8]={-2,-1,2,1,-2,-1,2,1};

struct node{
	int x;
	int y;
	int step;
};

bool check(int x,int y){
	if(x>=1 && x<=8 && y>=1 && y<=8) return true;
	else return false;
}

int bfs(){
	queue<node>q;
	node start;
	start.x=sx;
	start.y=sy;
	start.step=0;
	q.push(start);
	a[sx][sy]=1;
	while(!q.empty()){
		node now=q.front();
		q.pop();
		if(now.x==ex&&now.y==ey)//到达目标地点 
			return now.step;//返回步数 
		for(int i=0;i<8;i++){//8个方向 
			int nx=now.x+dirx[i];
			int ny=now.y+diry[i];
			if(check(nx,ny) && a[nx][ny]!=1){
				a[nx][ny]=1;
				node next;
				next.x=nx;
				next.y=ny;
				next.step=now.step+1;
				q.push(next);
			}
		}
	}
}

int main(){
	string s1,s2;
	while(cin >> s1 >> s2){
		memset(a,0,sizeof(a));
		sx=s1[0]-'a'+1,sy=s1[1]-'0';
		ex=s2[0]-'a'+1,ey=s2[1]-'0';
		cout << "To get from " << s1 << " to " << s2 << " takes " << bfs() << " knight moves." << endl;
	}
    return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值