宽度优先搜索

目录

P1135 奇怪的电梯

P1443 马的遍历

P1746 离开中山路

P2298 Mzc和男家丁的游戏

P1451 求细胞数量

P1596 [USACO10OCT]Lake Counting S

P1331 海战

P1506 拯救oibh总部

P1162 填涂颜色  

深さ優先探索

P2802 回家

P1144 最短路计数

P3395 路障

P1332 血色先锋队

P2895 [USACO08FEB]Meteor Shower S

P3663 [USACO17FEB]Why Did the Cow Cross the Road III S

P1141 01迷宫

P1767 家族

P1747 好奇怪的游戏

P3110 [USACO14DEC]Piggy Back S

P1825 [USACO11OPEN]Corn Maze S

P1189 `SEARCH`

P1902 刺杀大使

T203428 zhaojinxi的奥数作业

宣宣和学霸君的约会

P4667 [BalticOI 2011 Day1]Switch the Lamp On

P1032 [NOIP2002 提高组] 字串变换

P5195 [USACO05DEC]Knights of Ni S


P1135 奇怪的电梯

深搜做法

#include <bits/stdc++.h>
using namespace std;
int n, a, b, ans, flag, dis[201], vis[201]; 

void dfs(int x, int cnt){	//从x层开始,当前已经按了cnt次按钮
	if(x==b){	//如果已经到达B层
		flag=1;
		//如果当前的按钮次数小于保存的答案,替换
		if(cnt<ans)	
			ans=cnt;
		return;	 
	}
	//剪枝,因为还没到b,所以如果当前次数已经大于等于答案,无效
	if(cnt >= ans)	return;		
	//如果该楼层已访问过,或者到达该楼层之后不能再上再下,则跳过 
	if(vis[x])	return;		
	vis[x]=1;	//标记该楼层访问过 
	//如果按上不会超过n层,并且目标楼层没有访问过,可以向上走 
	if(x+dis[x]<=n && vis[x+dis[x]]==0){
		dfs(x+dis[x], cnt+1);
	}
	//如果按下不会到达1层以下,并且目标楼层没有访问过,可以向下走 
	if(x-dis[x]>=1 && vis[x-dis[x]]==0){
		dfs(x-dis[x], cnt+1);
	}		
	vis[x]=0;	//回溯	 			
	return;
}

int main()
{
	ans=1000000000;
	cin >> n >> a >> b;
	for(int i=1; i<=n; i++){
		cin >> dis[i];
	}
	//从第a层开始,刚开始按0次按钮 
	dfs(a, 0);
	if(flag==0)
		cout << "-1";
	else
		cout << ans;		
	return 0;	
} 

宽搜正确做法, vis[a]为true表示到过a层

#include <bits/stdc++.h>
using namespace std;
int n, a, b, k[210], dis[210], asd, nex;
bool vis[210];	//标记有没有到过这个楼层	 
queue<int> q; 	//里面放的楼层 
int main()
{
	//n层, 从a层到b层 
	scanf("%d %d %d", &n, &a, &b);
	for(int i=1; i<=n; ++i){
		scanf("%d", &k[i]);	//第i层可上、可下的层数 
	}
	q.push(a);
	vis[a]=true; //到过a层 
	while(!q.empty()){
		asd=q.front();	//获取队首楼层 
		if(asd==b){		//如果到终点楼层了 
			printf("%d", dis[asd]);
			return 0;
		} 
		nex=asd+k[asd];		//向上 
		if(nex<=n && !vis[nex]){	//没到过nex层, 而且没上天 
			vis[nex]=true;		//到过nex层 
			dis[nex]=dis[asd]+1;
			q.push(nex);
		}
		nex=asd-k[asd];		//向下 
		if(nex>=1 && !vis[nex]){	//没到过nex层, 而且没入地 
			vis[nex]=true;		//到过nex层 
			dis[nex]=dis[asd]+1;
			q.push(nex);
		}
		q.pop();		//一定记得出队 
	}
	printf("-1");
	return 0;
}

宽搜正确做法, vis[a]为true表示基于a层上下走过了

#include <bits/stdc++.h>
using namespace std;
int n, a, b, k[210], dis[210], asd, nex;
bool vis[210];	//标记有没有从这个楼层上、下走过	 
queue<int> q; 	//里面放的楼层 
int main()
{
	//n层, 从a层到b层 
	scanf("%d %d %d", &n, &a, &b);
	for(int i=1; i<=n; ++i){
		scanf("%d", &k[i]);	//第i层可上、可下的层数 
	}
	memset(dis, 0x3f, sizeof(dis));
	q.push(a);
	dis[a]=0;
	while(!q.empty()){
		asd=q.front();	//获取队首楼层 
		if(asd==b){		//如果到终点楼层了 
			printf("%d", dis[asd]);
			return 0;
		}
		if(!vis[asd]){	//如果还没从这个楼层上、下走过 
			vis[asd]=true;		//标记 
			nex=asd+k[asd];		//向上 
			if(nex<=n && !vis[nex]){	//没走过, 而且没上天 
				dis[nex]=min(dis[nex], dis[asd]+1);
				q.push(nex);
			}
			nex=asd-k[asd];		//向下 
			if(nex>=1 && !vis[nex]){	//没走过, 而且没入地 
				dis[nex]=min(dis[nex], dis[asd]+1);
				q.push(nex);
			}
		}
		q.pop();		//一定记得出队 
	}
	printf("-1");
	return 0;
}

宽搜错误做法1,知道为啥错吗?

#include <bits/stdc++.h>
using namespace std;
int n, a, b, k[210], dis[210], asd, nex;
bool vis[210];	//标记有没有从这个楼层上、下走过	 
queue<int> q; 	//里面放的楼层 
int main()
{
	//n层, 从a层到b层 
	scanf("%d %d %d", &n, &a, &b);
	for(int i=1; i<=n; ++i){
		scanf("%d", &k[i]);	//第i层可上、可下的层数 
	}
	q.push(a);
	while(!q.empty()){
		asd=q.front();	//获取队首楼层 
		if(asd==b){		//如果到终点楼层了 
			printf("%d", dis[asd]);
			return 0;
		}
		if(!vis[asd]){	//如果还没从这个楼层上、下走过 
			vis[asd]=true;		//标记 
			nex=asd+k[asd];		//向上 
			if(nex<=n && !vis[nex]){	//没走过, 而且没上天 
				dis[nex]=dis[asd]+1;
				q.push(nex);
			}
			nex=asd-k[asd];		//向下 
			if(nex>=1 && !vis[nex]){	//没走过, 而且没入地 
				dis[nex]=dis[asd]+1;
				q.push(nex);
			}
		}
		q.pop();		//一定记得出队 
	}
	printf("-1");
	return 0;
}

hack输入数据

200 1 200
1 1 26 7 9 21 10 5 12 13 3 15 3 26 2 9 28 12 24 10 21 26 22 10 5 10 14 8 25 9 15 5 27 9 24 30 15 27 25 1 5 5 16 1 18 1 24 20 24 22 17 7 21 18 29 20 30 8 21 9 3 24 15 27 16 18 29 21 11 1 22 30 24 23 6 5 28 24 18 26 21 9 3 19 9 27 5 9 17 29 6 5 9 6 18 15 9 5 19 23 23 3 3 2 4 24 25 12 19 14 23 15 11 25 25 5 3 3 2 6 21 7 18 8 11 26 10 10 20 21 28 10 15 9 24 23 17 22 13 17 18 27 21 4 15 13 4 2 17 21 3 28 26 21 28 9 22 23 15 20 5 7 29 11 4 30 10 2 4 27 17 21 4 21 1 10 28 17 10 14 28 11 11 15 3 26 23 6 28 15 4 27 13 7 25 18 3 19 28 0

hack输出数据

17

解释一下上面这个代码为啥错

vis[i]为true表示基于i楼层上下走过了

x为目标楼层

比如队列里有一个元素:p楼层

p楼层可以到达q楼层和x楼层,因为还没有基于q楼层和x楼层上、下走过, 于是有

dis[q]=dis[p]+1 和

dis[x]=dis[p]+1,这时已经算出了dis[x],但是x还没到队首,所以程序还得继续运行。

然后把q楼层和x楼层入队,p楼层出队

现在队首元素为q楼层,假设q楼层同样可以到达x楼层,虽然前面已经算出了一次dis[x],但是由于还没有基于x楼层上、下走过,所以这时还会继续更新一下dis[x], 变为dis[x]=dis[q]+1,并再次把x入队。然后q出队。

现在队首元素为x, 发现为目标楼层, 于是输出dis[x], 但是这时的dis[x]已经为dis[q]+1了,比最开始的dis[p]+1要大1,从而导致了答案错误。

在做这类找最少步数的宽搜题目时,一定要在第一次遇到问题元素的时候就把他标记,这样会避免目标被二次入队,从而避免二次计算的错误。

既然目标楼层会多次计算,多次入队,那我在目标楼层入队的时候就输出答案,并return 0是不是就可以了呢?于是有了下面的错误代码2:

宽搜错误做法2,知道为啥错吗?

#include <bits/stdc++.h>
using namespace std;
int n, a, b, k[210], dis[210], asd, nex;
bool vis[210];	//标记有没有从这个楼层上、下走过	 
queue<int> q; 	//里面放的楼层 
int main()
{
	//n层, 从a层到b层 
	scanf("%d %d %d", &n, &a, &b);
	for(int i=1; i<=n; ++i){
		scanf("%d", &k[i]);	//第i层可上、可下的层数 
	}
	q.push(a);
	while(!q.empty()){
		asd=q.front();	//获取队首楼层 
		if(asd==b){		//如果到终点楼层了 
			printf("%d", dis[asd]);
			return 0;
		}
		if(!vis[asd]){	//如果还没从这个楼层上、下走过 
			vis[asd]=true;		//标记 
			nex=asd+k[asd];		//向上 
			if(nex<=n && !vis[nex]){	//没走过, 而且没上天 
				dis[nex]=dis[asd]+1;
				q.push(nex);
				if(nex==b){		//如果到终点楼层了 
					printf("%d", dis[nex]);
					return 0;
				}
			}
			nex=asd-k[asd];		//向下 
			if(nex>=1 && !vis[nex]){	//没走过, 而且没入地 
				dis[nex]=dis[asd]+1;
				q.push(nex);
				if(nex==b){		//如果到终点楼层了 
					printf("%d", dis[nex]);
					return 0;
				}
			}
		}
		q.pop();		//一定记得出队 
	}
	printf("-1");
	return 0;
}

错误原因:虽然确保了目标楼层只入队了一次,但是无法确保其他楼层只入队一次,可能通往目标楼层的其他楼层多次入队了,从而导致目标楼层的步数不是最少的。

可以使用下面代码结合着上面的hack数据进行调试,会发现13层多次计算,多次入队。

#include <bits/stdc++.h>
using namespace std;
int n, a, b, k[210], dis[210], asd, nex;
bool vis[210];	//标记有没有从这个楼层上、下走过	 
queue<int> q; 	//里面放的楼层 
int main()
{
	freopen("asd.txt", "w", stdout);
	//n层, 从a层到b层 
	scanf("%d %d %d", &n, &a, &b);
	for(int i=1; i<=n; ++i){
		scanf("%d", &k[i]);	//第i层可上、可下的层数 
	}
	q.push(a);
	while(!q.empty()){
		asd=q.front();	//获取队首楼层 
		if(asd==b){		//如果到终点楼层了 
			printf("%d", dis[asd]);
			return 0;
		}
		if(!vis[asd]){	//如果还没从这个楼层上、下走过 
			vis[asd]=true;		//标记 
			nex=asd+k[asd];		//向上 
			if(nex<=n && !vis[nex]){	//没走过, 而且没上天 
				if(dis[nex] && dis[nex]<dis[asd]+1){
					cout << "asd" << asd << " " << nex << " " << dis[nex] << endl;
				}
				dis[nex]=dis[asd]+1;
				cout << asd << " " << nex << " " << dis[nex] << endl;
				q.push(nex);
				if(nex==b){		//如果到终点楼层了 
					printf("%d", dis[nex]);
					return 0;
				}
			}
			nex=asd-k[asd];		//向下 
			if(nex>=1 && !vis[nex]){	//没走过, 而且没入地 
				if(dis[nex] && dis[nex]<dis[asd]+1){
					cout << "asd" << asd << " " << nex << " " << dis[nex] << endl;
				}
				dis[nex]=dis[asd]+1;
				cout << asd << " " << nex << " " << dis[nex] << endl;
				q.push(nex);
				if(nex==b){		//如果到终点楼层了 
					printf("%d", dis[nex]);
					return 0;
				}
			}
		}
		q.pop();		//一定记得出队 
	}
	printf("-1");
	return 0;
}

P1443 马的遍历

深搜做法

#include <bits/stdc++.h>
using namespace std;
int f[500][500];
int m,n,x,y;
void dfs(int x,int y,int z){
	if(x>m || y>n || x<0 || y<0){
		return;
	}
	if(z>=f[x][y]){
		return;
	}
	f[x][y]=z;
	dfs(x-2,y+1,z+1);
	dfs(x+2,y+1,z+1);
	dfs(x+1,y+2,z+1);
	dfs(x-1,y+2,z+1);
	dfs(x-2,y-1,z+1);
	dfs(x+2,y-1,z+1);
	dfs(x+1,y-2,z+1);
	dfs(x-1,y-2,z+1);
}
int main(){
	scanf("%d %d %d %d",&m,&n,&x,&y);
	for(int i=1;i<=m;++i){
		for(int j=1;j<=n;++j){
			f[i][j]=1000;
		}
	}
	dfs(x,y,0);
	for(int i=1;i<=m;++i){
		for(int j=1;j<=n;++j){
			if(f[i][j]==1000){
				f[i][j]=-1;
				printf("%-5d",f[i][j]);
			}
			else{
				printf("%-5d",f[i][j]);
			}
		}
		printf("\n");
	}
	return 0;
}

宽搜做法

#include <bits/stdc++.h>
using namespace std;
queue<int> qx, qy;
int n, m, x, y, ans[401][401], vis[401][401];
int mx[8]={-2, -1, 1, 2, 2, 1, -1, -2};
int my[8]={1, 2, 2, 1, -1, -2, -2, -1};
//计算马(x, y) 到棋盘(a, b)的最短路径 
void bfs()
{
	qx.push(x);
	qy.push(y);
	vis[x][y]=1;
	ans[x][y]=0;
	while(!qx.empty() && !qy.empty())
	{
		int i=qx.front();
		int j=qy.front();
		for(int ix=0; ix<8; ix++)
		{
			if(i+mx[ix]>=1&&i+mx[ix]<=n&&j+my[ix]>=1&&j+my[ix]<=m&&vis[i+mx[ix]][j+my[ix]]==0)
			{
				qx.push(i+mx[ix]);
				qy.push(j+my[ix]);
				vis[i+mx[ix]][j+my[ix]]=1;
				ans[i+mx[ix]][j+my[ix]]=ans[i][j]+1;
			}
				
		}	
		qx.pop();
		qy.pop(); 
	}
}  
int main()
{
	cin >> n >> m >> x >> y;
	memset(ans, -1, sizeof ans);
	bfs();
	for(int i=1; i<=n; i++)
	{
		for(int j=1; j<=m; j++)
		{
			printf("%-5d", ans[i][j]);
		}
		cout << endl;
	}
	
	return 0;
}

P1746 离开中山路

#include <bits/stdc++.h>
using namespace std;
int n, a[1010][1010], dis[1010][1010], sx, sy, ex, ey;
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
bool vis[1010][1010];
queue<int> qx, qy;
void bfs()
{
	int x, y, nx, ny;
	qx.push(sx);
	qy.push(sy);
	vis[sx][sy]=0;
	while(!qx.empty()){
		x=qx.front();
		y=qy.front();
		qx.pop();
		qy.pop();
		if(x==ex && y==ey){
			printf("%d", dis[x][y]);
			exit(0);
		}
		for(int i=1; i<=4; ++i){
			nx=x+mx[i];
			ny=y+my[i];
			if(nx>=1 && nx<=n && ny>=1 && ny<=n && !vis[nx][ny] && !a[nx][ny]){
				qx.push(nx);
				qy.push(ny);
				vis[nx][ny]=true;
				dis[nx][ny]=dis[x][y]+1;
			}
		}	
	}
}
int main()
{
	scanf("%d", &n);
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=n; ++j){
			scanf("%1d", &a[i][j]);
		}
	}
	scanf("%d %d %d %d", &sx, &sy, &ex, &ey);
	bfs();
	return 0;
}

P2298 Mzc和男家丁的游戏

#include <bits/stdc++.h>
using namespace std;
int n, m, ans, sx, sy, x, y, nx, ny, asd;
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
char a[2010][2010];
bool vis[2010][2010];
struct node{
	int x, y, step;
};
queue<node> q;
void bfs()
{
	q.push({sx, sy, 0});
	vis[sx][sy]=true;
	while(!q.empty()){
		x=q.front().x;
		y=q.front().y;
		asd=q.front().step;
		if(a[x][y]=='d'){
			printf("%d", q.front().step);
			exit(0);
		}
		q.pop();
		for(int i=1; i<=4; ++i){
			nx=x+mx[i];
			ny=y+my[i];
			if(nx>=1 && nx<=n && ny>=1 && ny<=m && !vis[nx][ny] && (a[nx][ny]=='.'||a[nx][ny]=='d')){
				q.push({nx, ny, asd+1});
				vis[nx][ny]=true;
			}
		}
	}
}
int main()
{
	scanf("%d %d", &n, &m);	
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=m; ++j){
			cin >> a[i][j];
			if(a[i][j]=='m'){
				sx=i;
				sy=j;
			}
		}
	}
	bfs();
	printf("No Way!");
	return 0;
}

P1451 求细胞数量

找连通块数量

深搜做法。//宽搜做法同 P1596 [USACO10OCT]Lake Counting S 宽搜做法

#include <stdio.h>
bool vis[101][101];	//vis[i][j]=0,表示还没被包含 
int ans, n, m, a[101][101];
//深度优先搜索
//deep  first search 
void dfs(int x, int y)
{
	//上 x-1 y
	if(x-1>=1 && x-1<=n && y>=1 && y<=m && a[x-1][y]>=1 && a[x-1][y]<=9 && vis[x-1][y]==0){
		vis[x-1][y]=1;
		dfs(x-1, y);
	} 
	//下 x+1 y	
	if(x+1>=1 && x+1<=n && y>=1 && y<=m && a[x+1][y]>=1 && a[x+1][y]<=9 && vis[x+1][y]==0){
		vis[x+1][y]=1;
		dfs(x+1, y);
	} 
	//左 x y-1
	if(x>=1 && x<=n && y-1>=1 && y-1<=m && a[x][y-1]>=1 && a[x][y-1]<=9 && vis[x][y-1]==0){
		vis[x][y-1]=1;
		dfs(x, y-1);
	} 
	//左 x y+1	
	if(x>=1 && x<=n && y+1>=1 && y+1<=m && a[x][y+1]>=1 && a[x][y+1]<=9 && vis[x][y+1]==0){
		vis[x][y+1]=1;
		dfs(x, y+1);
	}
} 
int main()
{
	scanf("%d %d", &n, &m);
	for(int i=1; i<=n; i++){
		for(int j=1; j<=m; j++){
			scanf("%1d", &a[i][j]);
		}
	}
	for(int i=1; i<=n; i++){
		for(int j=1; j<=m; j++){
			if(a[i][j]>=1 && a[i][j]<=9 && vis[i][j]==0){
				ans++;
				vis[i][j]=1;
				//以第i行第j列为中心点,开始上下左右扩展 
				dfs(i, j); 
			}
		}
	} 
	printf("%d", ans);
	return 0; 
}


P1596 [USACO10OCT]Lake Counting S

双倍经验,找连通块数量

深搜做法

#include <bits/stdc++.h>
using namespace std;
char a[110][110];
bool vis[110][110];
int n, m, ans;
int mx[9]={0, -1, -1, 0, 1, 1, 1, 0, -1};
int my[9]={0, 0, 1, 1, 1, 0, -1, -1, -1};
//以x行y列为基准点, 向周围八个方向开始深搜 
void dfs(int x, int y)
{
	for(int i=1; i<=8; ++i){
		int xx=x+mx[i];
		int yy=y+my[i];
		if(a[xx][yy]=='W' && !vis[xx][yy]){
			vis[xx][yy]=true;
			dfs(xx, yy);
		}
	} 
}
int main()
{
	scanf("%d %d", &n, &m);
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=m; ++j){
			cin >> a[i][j];
		}
	}
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=m; ++j){
			if(a[i][j]=='W' && !vis[i][j]){
				ans++;
				vis[i][j]=true;
				dfs(i, j);
			}
		}
	}
	printf("%d", ans);
	return 0;
}

宽搜做法

#include <bits/stdc++.h>
using namespace std;
char a[110][110];
bool vis[110][110];
int n, m, ans;
int mx[9]={0, -1, -1, 0, 1, 1, 1, 0, -1};
int my[9]={0, 0, 1, 1, 1, 0, -1, -1, -1};
queue<int> qx, qy; 
//以x行y列为基准点, 向周围八个方向开始宽搜 
void bfs(int x, int y)
{
	qx.push(x);
	qy.push(y);
	vis[x][y]=true;
	while(!qx.empty()){
		x=qx.front();
		y=qy.front();
		for(int i=1; i<=8; ++i){
			int xx=x+mx[i];
			int yy=y+my[i];
			if(a[xx][yy]=='W' && !vis[xx][yy]){
				vis[xx][yy]=true;
				qx.push(xx);
				qy.push(yy);
			}
		} 
		qx.pop();
		qy.pop();
	}
}
int main()
{
	scanf("%d %d", &n, &m);
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=m; ++j){
			cin >> a[i][j];
		}
	}
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=m; ++j){
			if(a[i][j]=='W' && !vis[i][j]){
				ans++;
				bfs(i, j);
			}
		}
	}
	printf("%d", ans);
	return 0;
}

P5877 棋盘游戏

60分暴力分

#include <bits/stdc++.h>
using namespace std;
int n, m, a[501][501], c, u, v, asd;
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
bool vis[501][501];
queue<int> qx, qy;
void bfs(int x, int y)
{
	int curx, cury, nx, ny;
	vis[x][y]=true;
	qx.push(x);
	qy.push(y);
	while(!qx.empty()){
		curx=qx.front();
		cury=qy.front();
		qx.pop();
		qy.pop();
		for(int i=1; i<=4; ++i){
			nx=curx+mx[i];
			ny=cury+my[i];
			if(nx>=1 && nx<=n && ny>=1 && ny<=n && !vis[nx][ny] && a[nx][ny]!=-1 && a[nx][ny]==a[curx][cury]){
				vis[nx][ny]=true;
				qx.push(nx);
				qy.push(ny);
			}
		}
	}
}
int main()
{
	scanf("%d %d", &n, &m);
	memset(a, -1, sizeof(a));
	for(int i=1; i<=m; ++i){
		scanf("%d %d %d", &c, &u, &v);
		a[u][v]=c;
		memset(vis, 0, sizeof(vis));
		asd=0;
		for(int j=1; j<=n; ++j){
			for(int k=1; k<=n; ++k){
				if(a[j][k]!=-1 && !vis[j][k]){
					asd++;
					bfs(j, k);
				}
			}
		}
		printf("%d\n", asd);
	}
	return 0;
}

100分AC代码

P1331 海战

//找连通块数量,以及连通块里#的数量

#include <bits/stdc++.h>
using namespace std;
int r, c, ans, cur, mini, minj, maxi, maxj;
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
bool vis[1010][1010];
char a[1010][1010];
void dfs(int x, int y)
{
	for(int i=1; i<=4; ++i){
		int xx=x+mx[i];
		int yy=y+my[i];
		if(a[xx][yy]=='#' && vis[xx][yy]==0){
			cur++;
			mini=min(mini, xx);
			minj=min(minj, yy);
			maxi=max(maxi, xx);
			maxj=max(maxj, yy);
			vis[xx][yy]=1;
			dfs(xx, yy);
		}
	}
}
int main()
{
	scanf("%d %d", &r, &c);
	for(int i=1; i<=r; ++i){
		for(int j=1; j<=c; ++j){
			cin >> a[i][j];
		}
	}
	for(int i=1; i<=r; ++i){
		for(int j=1; j<=c; ++j){
			if(a[i][j]=='#' && vis[i][j]==0){
				ans++;
				cur=1;
				mini=maxi=i;
				minj=maxj=j;
				vis[i][j]=1;
				dfs(i, j);
				if(cur!=(maxi-mini+1)*(maxj-minj+1)){
					printf("Bad placement.");
					return 0;
				}
			}
		}
	}
	printf("There are %d ships.", ans);
	return 0;
}

P1506 拯救oibh总部

#include <bits/stdc++.h>
using namespace std;
int n, m, ans;
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
char a[510][510];
bool vis[510][510];
queue<int> qx, qy;
void bfs(int x, int y)
{
	int nx, ny;
	qx.push(x);
	qy.push(y);
	while(!qx.empty()){
		x=qx.front();
		y=qy.front();
		qx.pop();
		qy.pop();
		for(int i=1; i<=4; ++i){
			nx=x+mx[i];
			ny=y+my[i];
			if(!vis[nx][ny] && a[nx][ny]=='0'){		//不用判边界, 出界的不会等于'0' 
				qx.push(nx);
				qy.push(ny);
				vis[nx][ny]=true;
			}
		}
	}
}
int main()
{
	scanf("%d %d", &n, &m);
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=m; ++j){
			cin >> a[i][j];
		}
	}
	for(int j=1; j<=m; ++j){
		if(a[1][j]=='0'){
			vis[1][j]=true;
			bfs(1, j);
		}
		if(a[n][j]=='0'){
			vis[n][j]=true;
			bfs(n, j);
		}
	}
	for(int i=1; i<=n; ++i){
		if(a[i][1]=='0'){
			vis[i][1]=true;
			bfs(i, 1);
		}
		if(a[i][m]=='0'){
			vis[i][m]=true;
			bfs(i, m);
		}
	}
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=m; ++j){
			if(a[i][j]=='0' && !vis[i][j]){
				ans++;
			}
		}
	}
	printf("%d", ans);
	return 0;
}

P1162 填涂颜色  

双倍经验

方法1:四边去找0,挨个搜索

#include <bits/stdc++.h>
using namespace std;
int n;
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
int a[40][40];
bool vis[40][40];
queue<int> qx, qy;
void bfs(int x, int y)
{
	int nx, ny;
	qx.push(x);
	qy.push(y);
	while(!qx.empty()){
		x=qx.front();
		y=qy.front();
		qx.pop();
		qy.pop();
		for(int i=1; i<=4; ++i){
			nx=x+mx[i];
			ny=y+my[i];
			if(nx>=1 && nx<=n && ny>=1 && ny<=n && !vis[nx][ny] && a[nx][ny]==0){		
				qx.push(nx);
				qy.push(ny);
				vis[nx][ny]=true;
			}
		}
	}
}
int main()
{
	scanf("%d", &n);
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=n; ++j){
			scanf("%d", &a[i][j]);
		}
	}
	for(int j=1; j<=n; ++j){
		if(a[1][j]==0){
			vis[1][j]=true;
			bfs(1, j);
		}
		if(a[n][j]==0){
			vis[n][j]=true;
			bfs(n, j);
		}
	}
	for(int i=1; i<=n; ++i){
		if(a[i][1]==0){
			vis[i][1]=true;
			bfs(i, 1);
		}
		if(a[i][n]==0){
			vis[i][n]=true;
			bfs(i, n);
		}
	}
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=n; ++j){
			if(a[i][j]==0 && !vis[i][j]){
				printf("2 ");
			}
			else{
				printf("%d ", a[i][j]);
			}
		}
		printf("\n");
	}
	return 0;
}

方法2:用了点技巧和规律

#include <bits/stdc++.h>
using namespace std;
int n, flagx, flagy, a[32][32], vis[32][32];
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
//找到圈内某一个0,然后开始搜索,圈内的0构成一个连通块 
void dfs(int x, int y){
	if(x<0 || x>n+1 || y<0 || y>n+1 || a[x][y]==1 || vis[x][y]==1){
		return;
	}
	vis[x][y]=1;
	for(int i=1; i<=4; ++i){
		int nx=x+mx[i];
		int ny=y+my[i];
		if(nx>=0 && nx<=n+1 && ny>=0 && ny<=n+1 && a[x][y]==0 && vis[nx][ny]==0){
			dfs(nx,ny);
		}
	}
}
int main()
{
	cin >> n;
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=n; ++j){
			cin >> a[i][j];
		}
	}
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=n; ++j){
			if(a[i][j]){	//找到第一个1,右下方那个数必定在圈内 
				flagx=i+1;
				flagy=j+1;
				break;
			}
		}
		if(flagx!=0){
			break;
		}
	}
	//以圈内这个0开始搜索,所有可达的0肯定在圈内,都做标记 
	dfs(flagx,flagy);
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=n; ++j){
			if(vis[i][j]==1){
				a[i][j]=2;
			}
		}
	}
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=n; ++j){
			cout << a[i][j] << " ";
		}
		cout << endl;
	}
	return 0;
}

深さ優先探索

深搜做法

宽搜做法

#include <bits/stdc++.h>
using namespace std;
char a[510][510];
int n, m, startx, starty, desx, desy;	//起点行和列, 终点行和列 
int mx[5]= {0, -1, 1, 0, 0};		//行方向数组 
int my[5]= {0, 0, 0, -1, 1};		//列方向数组
bool vis[510][510];					
queue<int> qx, qy;
void bfs(int x, int y)
{
	int xx, yy;		
	qx.push(x);		//起点行入队 
	qy.push(y);		//起点列入队 
	vis[x][y]=true;	//入队时就标记为true 
	while(!qx.empty()){
		x=qx.front();	//获取队首元素 
		y=qy.front();
		if(x==desx && y==desy){	//如果到终点 
			printf("Yes");
			exit(0);	//结束整个程序 
		}
		for(int i=1; i<=4; ++i){	//枚举四个方向 
			xx=x+mx[i];
			yy=y+my[i];
			//如果是道路或者鱼店, 而且没走过 
			if((a[xx][yy]=='.' || a[xx][yy]=='g') && !vis[xx][yy]){
				qx.push(xx);	//入队 
				qy.push(yy);	//入队 
				vis[xx][yy]=true;		//入队马上标记 
			}
		}
		qx.pop();		//出队 
		qy.pop();
	}
}
int main() 
{
	scanf("%d %d", &n, &m);		//长和宽
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=m; ++j){
			cin >> a[i][j];
			if(a[i][j]=='s'){
				startx=i;
				starty=j;
			}
			if(a[i][j]=='g'){
				desx=i;
				desy=j;
			}
		}
	}
	bfs(startx, starty);
	printf("No");
	return 0;
}

P2802 回家

深搜做法

#include <iostream>
#include <cstdio>
using namespace std;
int a[11][11]={0}, visit[11][11]={0};
int n, m, startx, starty, life=6, ans=1000000; 
void dfs(int x, int y, int hp, int step)
{
	//遇到障碍物,或者此次位置已经搜索过 
	if(a[x][y]==0 || visit[x][y]==1 || step>=ans || step>2*m*n){
		return;	
	}
	//就算到了有鼠标的格子才死去,也不能通过拾取鼠标补满HP
	//即使在家门口死去,他也不能算完成任务回到家中。
	if(hp==0){
		return; 
	}
	//到家了 
	if(a[x][y]==3){ 
		if(step<ans){
			ans=step;
		}
		return;
	}	
	if(a[x][y]==4){	
//		visit[x][y]=1;
		dfs(x+1, y, 6, step+1); //遍历下边格子
		dfs(x, y+1, 6, step+1); //遍历右边格子
		dfs(x, y-1, 6, step+1); //遍历左边格子
		dfs(x-1, y, 6, step+1); //遍历上边格子 
//		visit[x][y]=0;
	} 
	if(a[x][y]==1 || a[x][y]==2){
//		visit[x][y]=1;
		dfs(x+1, y, hp-1, step+1); //遍历下边格子
		dfs(x, y+1, hp-1, step+1); //遍历右边格子
		dfs(x, y-1, hp-1, step+1); //遍历左边格子
		dfs(x-1, y, hp-1, step+1); //遍历上边格子
//		visit[x][y]=0;
	} 
}
int main()
{
	cin >> n >> m;
	for(int i=1; i<=n; i++){
		for(int j=1; j<=m; j++){
			cin >> a[i][j];
			if(a[i][j]==2){
				startx=i;
				starty=j;
			}
		}
	}
	dfs(startx, starty, 6, 0);
	if(ans==1000000)
		cout << "-1";
	else
		cout << ans;
	return 0;
}

宽搜做法

#include <bits/stdc++.h>
using namespace std;
int n, m, sx, sy, x, y, nx, ny, a[20][20], dis[20][20], blood[20][20];
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
bool vis[20][20];
queue<int> qx, qy;
void bfs()
{
	qx.push(sx);
	qy.push(sy);
	vis[sx][sy]=true;
	while(!qx.empty()){
		x=qx.front();
		y=qy.front();
		qx.pop();
		qy.pop();
		if(a[x][y]==3){		//到家了 
			printf("%d", dis[x][y]);
			exit(0);
		}
		for(int i=1; i<=4; ++i){
			nx=x+mx[i];
			ny=y+my[i];
			//没出边界, 没走过或者绕回来的血量更多, 在x,y处的血量大于1, 这样可以保证到了nx,ny处至少还有1的血量 
			if(nx>=1 && nx<=n && ny>=1 && ny<=m && (!vis[nx][ny] || vis[nx][ny]&&blood[nx][ny]<blood[x][y]-1) && a[nx][ny] && blood[x][y]>1){
				vis[nx][ny]=true;
				dis[nx][ny]=dis[x][y]+1;
				qx.push(nx);
				qy.push(ny);
				if(a[nx][ny]==4){
					blood[nx][ny]=6;
				}
				else{
					blood[nx][ny]=blood[x][y]-1;
				}
			}
		}
	}
}
int main()
{
	scanf("%d %d", &n, &m);
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=m; ++j){
			scanf("%d", &a[i][j]);
			if(a[i][j]==2){
				sx=i;
				sy=j;
			}
		}
	}
	blood[sx][sy]=6;	//初试血量 
	bfs();
	printf("-1");
	return 0;
}

贡献一组测试数据, 第10个测试点

.in

8 4
3 1 1 1
1 1 1 0
1 1 1 1
0 1 1 2
1 4 4 1
1 4 0 1
1 0 1 1
1 0 1 1

.out

8

P1144 最短路计数

#include <bits/stdc++.h>
using namespace std;
#define N 1000010 
#define mod 100003
vector<int> a[N];
queue<int> q;
bool vis[N];
int n, m, u, v, dis[N], ans[N];
void bfs()
{
	q.push(1);
	vis[1]=true;
	while(!q.empty()){
		u=q.front();
		q.pop();
		for(int i=0; i<a[u].size(); ++i){
			v=a[u][i];
			if(!vis[v]){	//第一次到达v点 
				dis[v]=dis[u]+1;	//找到从1号点到v号点的最短路了 
				q.push(v);
				vis[v]=true;
				ans[v]=ans[u]%mod;		//到v的方案数等于到u的方案数 
			}
			else if(dis[u]+1==dis[v]){	//之前已经找到了v的最短路, 并且这一分支也是最短路 
				ans[v]+=ans[u]%mod;			//加上1号点到u的方案数 
			}
		}
	}
}
int main()
{
	scanf("%d %d", &n, &m);
	for(int i=1; i<=m; ++i){
		scanf("%d %d", &u, &v);
		if(u!=v){
			a[u].push_back(v);
			a[v].push_back(u);
		} 
	}
	ans[1]=1;
	bfs();
	for(int i=1; i<=n; ++i){
		printf("%d\n", ans[i]%mod);
	}
	return 0;
}

P3395 路障

方法1:

#include <bits/stdc++.h>
using namespace std;
int n, t, x, y, nx, ny;
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
bool vis[1010][1010], flag;
int dis[1010][1010], asdx[2022], asdy[2022], step;
queue<int> qx, qy;
void bfs()
{
	qx.push(1);
	qy.push(1);
	vis[1][1]=true;
	while(!qx.empty()){
		x=qx.front();
		y=qy.front();
		qx.pop();
		qy.pop();
		if(x==n && y==n){
			flag=true;
			return;
		}
		//第step秒到的x,y  
		//接下来要从x,y向4个方向走, 也就是step秒后了 
		step=dis[x][y];	
		//如果没超2*n-2, 放置障碍物	
		if(step<=2*n-2){
			vis[asdx[step]][asdy[step]]=true;	
		}
		for(int i=1; i<=4; ++i){
			nx=x+mx[i];
			ny=y+my[i];
			//没出界、没走过、nx行ny列最早不能走的时间大于将要走的时间 
			if(nx>=1 && nx<=n && ny>=1 && ny<=n && !vis[nx][ny]){
				qx.push(nx);
				qy.push(ny);
				vis[nx][ny]=true;
				dis[nx][ny]=dis[x][y]+1;
			}
		}
	}
}
int main()
{
	scanf("%d", &t);
	while(t--){
		flag=false;
		memset(vis, 0, sizeof(vis));
		while(!qx.empty()){
			qx.pop();
			qy.pop();
		}
		scanf("%d", &n);
		for(int i=1; i<=2*n-2; ++i){
			scanf("%d %d", &x, &y);
			asdx[i]=x;	//i秒后有障碍 
			asdy[i]=y;	//i秒后有障碍 
		}
		bfs();
		if(flag){
			puts("Yes");
		}
		else{
			puts("No");
		}
	}
	return 0;
}

方法2:

#include <bits/stdc++.h>
using namespace std;
int n, t, x, y, nx, ny;
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
bool vis[1010][1010], flag;
int asd[1010][1010], dis[1010][1010];
queue<int> qx, qy;
void bfs()
{
	qx.push(1);
	qy.push(1);
	vis[1][1]=true;
	while(!qx.empty()){
		x=qx.front();
		y=qy.front();
		qx.pop();
		qy.pop();
		if(x==n && y==n){
			flag=true;
			return;
		}
		for(int i=1; i<=4; ++i){
			nx=x+mx[i];
			ny=y+my[i];
			//没出界、没走过、nx行ny列最早不能走的时间大于将要走的时间 
			if(nx>=1 && nx<=n && ny>=1 && ny<=n && !vis[nx][ny] && asd[nx][ny]>dis[x][y]+1){
				qx.push(nx);
				qy.push(ny);
				vis[nx][ny]=true;
				dis[nx][ny]=dis[x][y]+1;
			}
		}
	}
}
int main()
{
	scanf("%d", &t);
	while(t--){
		flag=false;
		memset(vis, 0, sizeof(vis));
		memset(asd, 0x3f, sizeof(asd));
		while(!qx.empty()){
			qx.pop();
			qy.pop();
		}
		scanf("%d", &n);
		for(int i=1; i<=2*n-2; ++i){
			scanf("%d %d", &x, &y);
			//x, y位置最早不能走的时间 
			asd[x][y]=min(i+1, asd[x][y]);	
		}
		bfs();
		if(flag){
			puts("Yes");
		}
		else{
			puts("No");
		}
	}
	return 0;
}

P1332 血色先锋队

#include <bits/stdc++.h>
using namespace std;
int n, m, a, b, x, y, dis[510][510];
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
bool vis[510][510];
queue<int> qx, qy; 
void bfs()
{
	while(!qx.empty()){
		x=qx.front();
		y=qy.front();
		for(int i=1; i<=4; ++i){
			int xx=x+mx[i];
			int yy=y+my[i];
			//没出界, 而且没走到过 
			if(xx>=1 && xx<=n && yy>=1 && yy<=m && !vis[xx][yy]){
				qx.push(xx);
				qy.push(yy);
				vis[xx][yy] =true;
				dis[xx][yy]=dis[x][y]+1;
			}
		} 
		qx.pop();
		qy.pop();
	}
}
int main()
{
	scanf("%d %d %d %d", &n, &m, &a, &b);
	memset(dis, 0x3f, sizeof(dis));
	for(int i=1; i<=a; ++i){
		scanf("%d %d", &x, &y);
		qx.push(x);
		qy.push(y);
		vis[x][y]=true;
		dis[x][y]=0;
	}
	bfs();
	for(int i=1; i<=b; ++i){
		scanf("%d %d", &x, &y);
		printf("%d\n", dis[x][y]);
	}
	return 0;
}

P2895 [USACO08FEB]Meteor Shower S

宽搜的经典题目

#include <bits/stdc++.h>
using namespace std;
int m, t;
bool vis[310][310];		//是否走过该点 
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1}; 
struct node
{
	bool safe=true;		//该点是否一直安全 
	int burnedtime;		//该点被烧成焦土, 不安全的最早时间 
}a[310][310];
queue<int> qx, qy, qt;
void bfs()
{
	int curx=0, cury=0, curtime=0, x, y;
	qx.push(curx);
	qy.push(cury);
	qt.push(curtime);
	vis[0][0]=true;
	while(!qx.empty()){
		x=qx.front();
		y=qy.front();
		curtime=qt.front();
		qx.pop();
		qy.pop();
		qt.pop();
		if(a[x][y].safe){	//如果该点始终安全 
			printf("%d", curtime);		//输出,结束程序 
			exit(0);	
		}
		//虽然该点不安全, 但是现在还没有被烧焦, 那么现在还可以从该点考虑走向四周 
		if(!a[x][y].safe && a[x][y].burnedtime>curtime){
			for(int i=1; i<=4; ++i){	//枚举上下左右 
				int xx=x+mx[i];
				int yy=y+my[i];
				if(xx>=0 && yy>=0){		//在第一象限移动, 可以出(300, 300)
					//如果该点没被走过, 并且安全, 或者下一步还没被烧焦, 则该点可以走 
					if(!vis[xx][yy] && a[xx][yy].safe || !vis[xx][yy] && !a[xx][yy].safe && a[xx][yy].burnedtime>curtime+1){
						qx.push(xx);
						qy.push(yy);
						qt.push(curtime+1);
						vis[xx][yy]=true;
					}
				}
			}
		}
	}
}
int main()
{
	scanf("%d", &m);
	for(int i=1; i<=m; ++i){
		int x, y; 
		scanf("%d %d %d", &x, &y, &t);	//流星砸下来的坐标及砸下来的时间 
		if(a[x][y].safe){		//如果该点没被烧焦 
			a[x][y].safe=false;		//标记被烧焦 
			a[x][y].burnedtime=t;	//被烧焦的最早时间记为t 
		}
		else if(!a[x][y].safe && a[x][y].burnedtime>t){		//如果该点被烧焦, 但是被烧焦的时间比较晚, 更新成更早的时间t 
			a[x][y].burnedtime=t;
		}
		//枚举上下左右 
		for(int j=1; j<=4; ++j){
			int xx=x+mx[j];
			int yy=y+my[j];
			if(xx>=0 && yy>=0){		//在第一象限移动, 可以出(300, 300), 所以标记烧焦点时也只限制第一象限 
				if(a[xx][yy].safe){
					a[xx][yy].safe=false;
					a[xx][yy].burnedtime=t;
				}
				else if(!a[xx][yy].safe && a[xx][yy].burnedtime>t){
					a[xx][yy].burnedtime=t;
				}
			}
		}
	}
	bfs();
	printf("-1");
	return 0;
}

提供一组洛谷第6个测试点的数据

.in

61
20 3 22
2 6 6
15 2 22
9 2 22
11 3 21
18 0 32
14 2 25
0 6 4
14 6 16
16 1 32
9 1 22
5 8 16
14 3 16
38 3 22
3 0 3
8 3 18
14 9 18
4 5 11
21 0 36
29 3 22
10 0 25
4 7 12
5 6 15
4 9 16
0 10 14
11 6 18
17 3 22
6 1 4
41 3 22
10 4 21
3 10 14
7 8 16
12 1 25
22 0 36
20 2 22
4 5 15
18 1 32
9 10 14
1 1 2
8 5 16
3 8 14
11 8 18
1 4 7
32 3 22
24 0 38
0 3 5
6 4 6
14 0 32
25 2 36
1 2 4
26 3 22
35 3 22
6 10 14
23 3 22
23 2 22
11 5 18
9 7 17
5 6 17
0 0 2
6 6 16
3 3 8

.out

38

P3663 [USACO17FEB]Why Did the Cow Cross the Road III S

深搜做法

#include <bits/stdc++.h>
using namespace std;
int n, k, r, a[110][110], r1, c1, r2, c2, x, y, ans;
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
int asd[10010], cnt;	//每个连通块里面牛的数量, 连通块的数量 
bool vis[110][110];
bool connected[110][110][5];	//connected[i][j][k]表示第i行j列的牛上下左右是否有马路 
void dfs(int x, int y)
{
	int xx, yy;
	for(int i=1; i<=4; ++i){
		xx=x+mx[i];
		yy=y+my[i];
		//x行y列这头牛的上、下、左、右方向没有马路 
		if(!connected[x][y][i] && xx>=1 && xx<=n && yy>=1 && yy<=n && !vis[xx][yy]){
			asd[cnt]+=a[xx][yy];	//更新连通块里牛的数量 
			vis[xx][yy]=true;		
			dfs(xx, yy);
		}
	}
}
int main()
{
	scanf("%d %d %d", &n, &k, &r);
	for(int i=1; i<=r; ++i){
		scanf("%d %d %d %d", &r1, &c1, &r2, &c2);
		if(r1==r2){
			if(c1<c2){
				connected[r1][c1][4]=true;	//r1,c1往右有马路
				connected[r2][c2][3]=true;	//r2,c2往左有马路
			}
			else{
				connected[r2][c2][4]=true;	//r2,c2往右有马路 
				connected[r1][c1][3]=true;	//r1,c1往左有马路 
			}
		}
		else{	//c1=c2
			if(r1<r2){
				connected[r1][c1][2]=true;	//r1,c1往下有马路
				connected[r2][c2][1]=true;	//r2,c2往上有马路
			}
			else{
				connected[r2][c2][2]=true;	//r2,c2往下有马路 
				connected[r1][c1][1]=true;	//r1,c1往上有马路 
			}
		}
	}
	for(int i=1; i<=k; ++i){
		scanf("%d %d", &x, &y);
		a[x][y]=1;		//牛 
	} 
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=n; ++j){
			if(a[i][j]==1 && !vis[i][j]){	//有牛, 并且没访问过 
				vis[i][j]=true;		//标记访问过 
				cnt++;				//连通块编号++ 
				asd[cnt]=1;			//连通块里牛的数量为1 
				dfs(i, j);			//深搜	
			}
		}
	}
	for(int i=1; i<cnt; ++i){
		for(int j=i+1; j<=cnt; ++j){
			ans+=asd[i]*asd[j];
		}
	}
	printf("%d", ans);
	return 0;
}

宽搜做法

#include <bits/stdc++.h>
using namespace std;
int n, k, r, a[110][110], r1, c1, r2, c2, x, y, ans;
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
int asd[10010], cnt;	//每个连通块里面牛的数量, 连通块的数量 
bool vis[110][110];
bool connected[110][110][5];	//connected[i][j][k]表示第i行j列的牛上下左右是否有马路 
queue<int> qx, qy;
void bfs(int x, int y)		//从x,y开始宽搜 
{
	int xx, yy;
	qx.push(x);
	qy.push(y);
	while(!qx.empty()){
		x=qx.front();
		y=qy.front();
		for(int i=1; i<=4; ++i){
			xx=x+mx[i];
			yy=y+my[i];
			//x行y列这头牛的上、下、左、右方向没有马路 
			if(!connected[x][y][i] && xx>=1 && xx<=n && yy>=1 && yy<=n && !vis[xx][yy]){
				asd[cnt]+=a[xx][yy];	//更新连通块里牛的数量 
				vis[xx][yy]=true;		
				qx.push(xx);
				qy.push(yy);
			}
		}	
		qx.pop();
		qy.pop();
	}
}
int main()
{
	scanf("%d %d %d", &n, &k, &r);
	for(int i=1; i<=r; ++i){
		scanf("%d %d %d %d", &r1, &c1, &r2, &c2);
		if(r1==r2){
			if(c1<c2){
				connected[r1][c1][4]=true;	//r1,c1往右有马路
				connected[r2][c2][3]=true;	//r2,c2往左有马路
			}
			else{
				connected[r2][c2][4]=true;	//r2,c2往右有马路 
				connected[r1][c1][3]=true;	//r1,c1往左有马路 
			}
		}
		else{	//c1=c2
			if(r1<r2){
				connected[r1][c1][2]=true;	//r1,c1往下有马路
				connected[r2][c2][1]=true;	//r2,c2往上有马路
			}
			else{
				connected[r2][c2][2]=true;	//r2,c2往下有马路 
				connected[r1][c1][1]=true;	//r1,c1往上有马路 
			}
		}
	}
	for(int i=1; i<=k; ++i){
		scanf("%d %d", &x, &y);
		a[x][y]=1;		//牛 
	} 
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=n; ++j){
			if(a[i][j]==1 && !vis[i][j]){	//有牛, 并且没访问过 
				vis[i][j]=true;		//标记访问过 
				cnt++;				//连通块编号++ 
				asd[cnt]=1;			//连通块里牛的数量为1 
				bfs(i, j);			//宽搜找连通块	
			}
		}
	}
	for(int i=1; i<cnt; ++i){
		for(int j=i+1; j<=cnt; ++j){
			ans+=asd[i]*asd[j];
		}
	}
	printf("%d", ans);
	return 0;
}

P1141 01迷宫

求连通块里格子的数量

深搜做法

#include <bits/stdc++.h>
using namespace std;
//ans[i][j]表示第i行第j列能移动到的格子数
//curmax表示当前板块中的格子数 
//a[][]数组存迷宫 
int n, m, x, y, a[1001][1001], ans[1001][1001], curmax=1;
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
//vis[i][j]=1表示第i行第j列的格子已经被加入到某一板块中 
bool vis[1001][1001];
queue<int> xqueue, yqueue;
//本代码中能dfs到的,必定是在迷宫范围内能到达的 
//遍历第几行第几列 
void dfs(int row, int column)
{ 
	int nx, ny;
	//枚举上下左右 
	for(int i=1; i<=4; ++i){
		nx=row+mx[i];
		ny=column+my[i];
		//没出界, 并且能到达, 并且没走过 
		if(nx>=1 && nx<=n && ny>=1 && ny<=n && a[row][column]^a[nx][ny] && !vis[nx][ny]){
			vis[nx][ny]=true;		//标记 
			xqueue.push(nx);		//入队 
			yqueue.push(ny);	
			curmax++;				//格子数++ 
			dfs(nx, ny);			//深搜 
		}
	}
}
int main()
{
	scanf("%d %d", &n, &m); 
	//输入迷宫 
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=n; ++j){
			scanf("%1d", &a[i][j]);
		}
	}
	//遍历每个没有被标记过的格子  
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=n; ++j){
			//如果第i行j列没被访问过,则说明找到一个全新的板块,从这一个位置开始搜索 
			if(vis[i][j]==0){
				//把上一个板块的格子处理一下 
				while(!xqueue.empty()){
					int xx=xqueue.front();
					int yy=yqueue.front();
					//这个板块的格子数都是等于curmax 
					ans[xx][yy]=curmax;
					xqueue.pop(); 
					yqueue.pop();
				} 
				//开始找新的板块,初始化curmax为1,将新板块的第一个位置标记为已经访问过
				//并把该位置的横纵坐标添加到队列中 
				curmax=1; 
				vis[i][j]=1;
				xqueue.push(i);
				yqueue.push(j);
				dfs(i, j); 
			} 
		}
	} 
	//处理最后一个板块 
	while(!xqueue.empty()){
		int xx=xqueue.front();
		int yy=yqueue.front();
		ans[xx][yy]=curmax;
		xqueue.pop(); 
		yqueue.pop();
	} 
	for(int i=1; i<=m; ++i){
		scanf("%d %d", &x, &y);
		printf("%d\n", ans[x][y]);
	}
	return 0;
}

宽搜做法

#include <bits/stdc++.h>
using namespace std;
//ans[i][j]表示第i行第j列能移动到的格子数
//curmax表示当前板块中的格子数 
//a[][]数组存迷宫 
int n, m, x, y, a[1001][1001], ans[1001][1001], curmax=1;
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
//vis[i][j]=1表示第i行第j列的格子已经被加入到某一板块中 
bool vis[1001][1001];
queue<int> xqueue, yqueue, qx, qy;
//本代码中能bfs到的,必定是在迷宫范围内能到达的 
//基于第几行第几列去宽搜 
void bfs(int row, int column)
{ 
	int xx, yy, nx, ny;
	qx.push(row);
	qy.push(column);
	while(!qx.empty()){
		xx=qx.front();
		yy=qy.front();
		qx.pop();
		qy.pop(); 
		//枚举上下左右 
		for(int i=1; i<=4; ++i){
			nx=xx+mx[i];
			ny=yy+my[i];
			//没出界, 并且能到达, 并且没走过 
			if(nx>=1 && nx<=n && ny>=1 && ny<=n && a[xx][yy]^a[nx][ny] && !vis[nx][ny]){
				vis[nx][ny]=true;		//标记 
				xqueue.push(nx);		//入队 
				yqueue.push(ny);	
				curmax++;				//格子数++ 
				qx.push(nx);			//宽搜入队
				qy.push(ny);			//宽搜入队 
			}
		}
	}
	//宽搜完更新该连通块里每个格子的答案 
	while(!xqueue.empty()){
		xx=xqueue.front();
		yy=yqueue.front();
		ans[xx][yy]=curmax;
		xqueue.pop(); 
		yqueue.pop();
	} 
}
int main()
{
	scanf("%d %d", &n, &m); 
	//输入迷宫 
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=n; ++j){
			scanf("%1d", &a[i][j]);
		}
	}
	//遍历每个没有被标记过的格子  
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=n; ++j){
			//如果第i行j列没被访问过,则说明找到一个全新的板块,从这一个位置开始搜索 
			if(vis[i][j]==0){
				//开始找新的板块,初始化curmax为1,将新板块的第一个位置标记为已经访问过
				//并把该位置的横纵坐标添加到队列中 
				curmax=1; 
				vis[i][j]=1;
				xqueue.push(i);
				yqueue.push(j);
				bfs(i, j); 
			} 
		}
	} 
	for(int i=1; i<=m; ++i){
		scanf("%d %d", &x, &y);
		printf("%d\n", ans[x][y]);
	}
	return 0;
}

P1767 家族

字符串和求连通块数量结合的一道题目

注意细节:当先输入一个整数n, 然后输入n行字符串时,需要在输入n后加两个getchar(),不然会出错。

#include <bits/stdc++.h>
using namespace std;
int n, m=200, len, ans;
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
string s;
char a[110][210];
bool vis[110][210];
queue<int> qx, qy; 
void bfs(int x, int y)
{
	int nx, ny;
	qx.push(x);
	qy.push(y);
	while(!qx.empty()){
		x=qx.front();
		y=qy.front();
		qx.pop();
		qy.pop();
		for(int i=1; i<=4; ++i){
			nx=x+mx[i];
			ny=y+my[i];
			if(nx>=1 && nx<=n && ny>=1 && ny<=m && !vis[nx][ny] && a[nx][ny]>='a' && a[nx][ny]<='z'){
				vis[nx][ny]=true;
				qx.push(nx);
				qy.push(ny);
			}
		}
	}
}
int main()
{
	scanf("%d", &n);
	getchar();
	getchar();
	for(int i=1; i<=n; ++i){
		getline(cin, s);
		len=s.length();
		for(int j=1; j<=len; ++j){
			a[i][j]=s[j-1];
		}
	}
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=m; ++j){
			if(a[i][j]>='a' && a[i][j]<='z' && !vis[i][j]){
				ans++;
				vis[i][j]=true;
				bfs(i, j);
			}
		}
	}
	printf("%d", ans);
	return 0;
}

P1747 好奇怪的游戏

#include <bits/stdc++.h>
using namespace std;
int a[30][30], h1x, h1y, h2x, h2y, cnt, dis[30][30];
int mx[13]={0, -2, -2, -1, 1, 2, 2, 2, 2, 1, -1, -2, -2};
int my[13]={0, 1, 2, 2, 2, 2, 1, -1, -2, -2, -2, -2, -1};
bool vis[30][30];
queue<int> qx, qy;
void bfs()
{
	int x, y, nx, ny;
	//从(1,1)反过来去找两匹马,本题不考虑两匹马的相互影响 
	qx.push(1);
	qy.push(1);
    vis[1][1]=true;
	while(!qx.empty()){
		x=qx.front();
		y=qy.front();
		if(x==h1x && y==h1y){
			cnt++;
		}
		if(x==h2x && y==h2y){
			cnt++;
		}
		if(cnt==2){
			return;
		}
		qx.pop();
		qy.pop();
		for(int i=1; i<=12; ++i){
			nx=x+mx[i];
			ny=y+my[i];
			if(nx>=1 && ny>=1 && nx<=22 && ny<=22 && !vis[nx][ny]){
				qx.push(nx);
				qy.push(ny);
				vis[nx][ny]=true;
				dis[nx][ny]=dis[x][y]+1;
			}
		}
	}
}
int main()
{
	scanf("%d %d", &h1x, &h1y);
	scanf("%d %d", &h2x, &h2y);
	bfs();
	printf("%d\n%d", dis[h1x][h1y], dis[h2x][h2y]);
	return 0;
}

P3110 [USACO14DEC]Piggy Back S

#include <bits/stdc++.h>
using namespace std;
int b, e, p, n, m, u, v, len;
long long dis[40010], dis1[40010], dis2[40010], ans=1e10; 
bool vis[40010], vis1[40010], vis2[40010]; 
vector<int> a[40010];
queue<int> q;
//计算n到其他仓库的最短距离 
void bfs()
{
	q.push(n);
	vis[n]=true;
	while(!q.empty()){
		u=q.front();
		q.pop();
		len=a[u].size();
		for(int i=0; i<len; ++i){
			v=a[u][i];
			if(!vis[v]){
				q.push(v);
				vis[v]=true;
				dis[v]=dis[u]+1;
			}
		}
	}
}
//计算Bessie从1到其他仓库的最短距离 
void bfs1()
{
	q.push(1);
	vis1[1]=true;
	while(!q.empty()){
		u=q.front();
		q.pop();
		len=a[u].size(); 
		for(int i=0; i<len; ++i){
			v=a[u][i];
			if(!vis1[v]){
				q.push(v);
				vis1[v]=true;
				dis1[v]=dis1[u]+1;
			}
		}
	} 
}
//计算Elsie从2到其他仓库的最短距离 
void bfs2()
{
	q.push(2);
	vis2[2]=true;
	while(!q.empty()){
		u=q.front();
		q.pop();
		len=a[u].size(); 
		for(int i=0; i<len; ++i){
			v=a[u][i];
			if(!vis2[v]){
				q.push(v);
				vis2[v]=true;
				dis2[v]=dis2[u]+1;
			}
		}
	} 
}
int main()
{
	//Bessie消耗能量, Elsie消耗能量, 背着消耗能量, 谷仓数量, 道路数 
	scanf("%d %d %d %d %d", &b, &e, &p, &n, &m);
	while(m--){
		scanf("%d %d", &u, &v);
		a[u].push_back(v);		//双向建边 
		a[v].push_back(u);
	}
	bfs();		//计算n到其他仓库的最短距离 
	bfs1();		//计算Bessie从1到其他仓库的最短距离
	bfs2(); 	//计算Elsie从2到其他仓库的最短距离
	if(b+e<=p){		//背着走, 不如独立走 
		//从n出发, 搜到1和2				
		ans=dis[1]*b+dis[2]*e;
	}
	else{			//先走到同一个位置相遇, 然后背着一起走
		for(int i=1; i<=n; ++i){	//i号仓库相遇 
			if(vis1[i] && vis2[i]){		//都能到i号仓库 
				ans=min(ans, dis1[i]*b+dis2[i]*e+dis[i]*p);
			}
		}
	}
	printf("%lld", ans);
	return 0;
}

P1825 [USACO11OPEN]Corn Maze S

毒瘤题,传送装置没有起始、终点之分,从其他格子走到传送装置,那么这个传送装置就是起点,就只能传送到与之相对应的终点,然而传送到了终点后,终点这个位置可以而且必须上下左右四个方向走的,是没有必要马上传回起点的,如果马上传回起点,来来回回就死循环了。 

所以在遇到A~Z时,需要入队,并标记,这时候的传送装置是起点。当这个位置到队首并出队时, 就需要传送到终点,而不是上下左右走。这时候到终点之后需要上下左右四个位置去走。

#include <bits/stdc++.h>
using namespace std;
int n, m, sx, sy, ex, ey, id, dis[310][310], asdx[27][3], asdy[27][3];
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
char a[310][310];
bool vis[310][310];
queue<int> qx, qy;
void bfs()
{
	int x, y, nx, ny;
	qx.push(sx);
	qy.push(sy);
	vis[sx][sy]=true;
	while(!qx.empty()){
		x=qx.front();
		y=qy.front();
		qx.pop();
		qy.pop();
		if(x==ex && y==ey){ 
			printf("%d", dis[x][y]);
			exit(0);
		}
		if(a[x][y]>='A' && a[x][y]<='Z'){	//传送装置 
			id=a[x][y]-'A'+1;
			if(x==asdx[id][1] && y==asdy[id][1]){
				nx=asdx[id][2];
				ny=asdy[id][2];
			}
			else{
				nx=asdx[id][1];
				ny=asdy[id][1];
			}
			dis[nx][ny]=dis[x][y];
			x=nx;
			y=ny;
		}
		for(int i=1; i<=4; ++i){
			nx=x+mx[i];
			ny=y+my[i];
			//没出界, 没被访问过 
			if(nx>=1 && nx<=n && ny>=1 && ny<=m && !vis[nx][ny]){
				//草地 或 出口 
				if(a[nx][ny]=='.' || a[nx][ny]=='=' || a[nx][ny]>='A'&&a[nx][ny]<='Z'){	
					qx.push(nx);
					qy.push(ny);
					vis[nx][ny]=true;
					dis[nx][ny]=dis[x][y]+1;
				}  
			}
		}
	}
} 
int main()
{
	scanf("%d %d", &n, &m);
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=m; ++j){
			cin >> a[i][j];
			if(a[i][j]=='@'){	//入口 
				sx=i;
				sy=j;
			}
			if(a[i][j]=='='){	//出口 
				ex=i;
				ey=j;
			}
			if(a[i][j]>='A' && a[i][j]<='Z'){
				id=a[i][j]-'A'+1;
				if(asdx[id][1]){	//第一个传送横坐标已经有了 
					asdx[id][2]=i;	//说明是第二个传送装置 
					asdy[id][2]=j;
				}
				else{			
					asdx[id][1]=i;	//第一个传送装置 
					asdy[id][1]=j;
				}
			}
		}
	}
	bfs(); 
	return 0;
}

P1189 `SEARCH`

非常好的一道宽搜题

法1:使用队列数组

#include <bits/stdc++.h>
using namespace std;
int r, c, n, sx, sy, id, x, y, nx, ny;
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
map<string, int> direction;
char a[60][60];
string asd;
queue<int> qx[1010], qy[1010];
bool vis[1010][51][51];
int main()
{
	direction["NORTH"]=1;	//上北 
	direction["SOUTH"]=2;	//下南 
	direction["WEST"]=3;	//左西 
	direction["EAST"]=4;	//右东 
	scanf("%d %d", &r, &c);
	for(int i=1; i<=r; ++i){
		for(int j=1; j<=c; ++j){
			cin >> a[i][j];
			if(a[i][j]=='*'){
				sx=i;
				sy=j;
			}
		}
	}
	a[sx][sy]='.';
	qx[0].push(sx);
	qy[0].push(sy);
	vis[0][sx][sy]=true;
	scanf("%d", &n);
	for(int i=1; i<=n; ++i){
		cin >> asd;		//方向 
		id=direction[asd];	//方向转下标
		while(!qx[i-1].empty()){	//上一步可能的位置不为空 
			x=qx[i-1].front();
			y=qy[i-1].front();
			qx[i-1].pop();
			qy[i-1].pop();
			nx=x+mx[id];
			ny=y+my[id];
			//没出界, 而且可以走 
			while(nx>=1 && nx<=r && ny>=1 && ny<=c && !vis[i][nx][ny] && a[nx][ny]=='.'){
				qx[i].push(nx);
				qy[i].push(ny);
				vis[i][nx][ny]=true;
				nx+=mx[id];
				ny+=my[id];
			}
		}
	} 
	while(!qx[n].empty()){
		x=qx[n].front();
		y=qy[n].front();
		qx[n].pop();
		qy[n].pop();
		a[x][y]='*';
	}
	for(int i=1; i<=r; ++i){
		for(int j=1; j<=c; ++j){
			cout << a[i][j];
		}
		cout << endl;
	}
	return 0;
}

法2:使用1个队列

#include <bits/stdc++.h>
using namespace std;
int r, c, n, sx, sy, id, x, y, nx, ny, dis;
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
map<string, int> direction;
char a[60][60];
string asd;
queue<int> qx, qy, qdis;
bool vis[1010][51][51];		//vis[cnt][i][j]表示第cnt个方向, 有没有到过i行,j列 
int main()
{
	direction["NORTH"]=1;	//上北 
	direction["SOUTH"]=2;	//下南 
	direction["WEST"]=3;	//左西 
	direction["EAST"]=4;	//右东 
	scanf("%d %d", &r, &c);
	for(int i=1; i<=r; ++i){
		for(int j=1; j<=c; ++j){
			cin >> a[i][j];
			if(a[i][j]=='*'){
				sx=i;
				sy=j;
			}
		}
	}
	a[sx][sy]='.';
	qx.push(sx);
	qy.push(sy);
	qdis.push(0);
	vis[0][sx][sy]=true;
	scanf("%d", &n);
	for(int i=1; i<=n; ++i){
		cin >> asd;		//方向 
		id=direction[asd];	//方向转下标
		while(!qdis.empty() && qdis.front()<i){	//上一步可能的位置不为空 
			x=qx.front();
			y=qy.front();
			dis=qdis.front();
			qx.pop();
			qy.pop();
			qdis.pop();
			nx=x+mx[id];
			ny=y+my[id];
			//没出界, 而且可以走 
			while(nx>=1 && nx<=r && ny>=1 && ny<=c && !vis[i][nx][ny] && a[nx][ny]=='.'){
				qx.push(nx);
				qy.push(ny);
				qdis.push(dis+1);
				vis[i][nx][ny]=true;
				nx+=mx[id];
				ny+=my[id];
			}
		}
	} 
	while(!qx.empty()){
		x=qx.front();
		y=qy.front();
		qx.pop();
		qy.pop();
		a[x][y]='*';
	}
	for(int i=1; i<=r; ++i){
		for(int j=1; j<=c; ++j){
			cout << a[i][j];
		}
		cout << endl;
	}
	return 0;
}

P1902 刺杀大使

测试点1.in

10 10
0 0 0 0 0 0 0 0 0 0 
3 3 8 7 3 6 7 5 8 7 
1 3 7 4 5 9 9 9 2 10 
3 9 2 7 9 7 1 1 7 9 
3 9 8 2 4 3 5 8 9 6 
1 7 1 6 9 5 9 1 5 1 
10 4 1 5 7 6 1 1 6 7 
10 8 6 5 3 7 10 9 7 8 
6 8 7 6 5 4 1 3 10 2 
0 0 0 0 0 0 0 0 0 0 

测试点1.out

7

方法1:优先队列+宽搜

#include <bits/stdc++.h>
using namespace std;
int n, m, x, y, w, nx, ny;
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
int p[1010][1010], dis[1010][1010], ans=1e15;
bool vis[1010][1010];
struct node
{
	int x, y, w;
};
priority_queue<node> q;
bool operator < (node a, node b)
{
	return a.w>b.w;
}
void bfs()
{
	for(int i=1; i<=m; ++i){
		vis[1][i]=true;
		vis[2][i]=true;
		q.push({2, i, dis[2][i]});
	}
	while(!q.empty()){
		x=q.top().x;
		y=q.top().y;
		w=q.top().w;
		q.pop();
		vis[x][y]=false;
		for(int i=1; i<=4; ++i){
			nx=x+mx[i];
			ny=y+my[i];
			if(nx>=1 && nx<=n && ny>=1 && ny<=m && max(w, p[nx][ny])<dis[nx][ny]){
				dis[nx][ny]=max(w, p[nx][ny]);
				if(!vis[nx][ny]){
					q.push({nx, ny, dis[nx][ny]});	
					vis[nx][ny]=true;
				}
			}
		}
	}
}
int main()
{
	scanf("%d %d", &n, &m);
	memset(dis, 0x3f, sizeof(dis));
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=m; ++j){
			scanf("%lld", &p[i][j]);
			dis[2][j]=p[2][j];
		}
	}
	bfs();
	for(int i=1; i<=m; ++i){
		ans=min(ans, dis[n][i]);
	}
	printf("%lld", ans);
	return 0;
}

方法2:宽搜+暴力, 100分A了

#include <bits/stdc++.h>
using namespace std;
int n, m, x, y, nx, ny, l, r, mid;
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
int p[1010][1010], ans;
bool vis[1010][1010];
queue<int> qx, qy;
bool bfs(int asd)
{
	while(!qx.empty()){
		qx.pop();
		qy.pop();
	}
	memset(vis, 0, sizeof(vis));
	for(int i=1; i<=n; ++i){
		qx.push(1);
		qy.push(i);	
		vis[1][i]=true;
	}
	while(!qx.empty()){
		x=qx.front();
		y=qy.front();
		qx.pop();
		qy.pop();
		if(x==n){
			return true;
		}
		for(int i=1; i<=4; ++i){
			nx=x+mx[i];
			ny=y+my[i];
			if(nx>=1 && nx<=n && ny>=1 && ny<=m && p[nx][ny]<=asd && !vis[nx][ny]){
				vis[nx][ny]=true;
				qx.push(nx);
				qy.push(ny);	
			}
		}
	}
	return false;
}
int main()
{
	scanf("%d %d", &n, &m);
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=m; ++j){
			scanf("%d", &p[i][j]);
			r=max(r, p[i][j]);
		}
	}
	for(int i=0; i<=r; ++i){
		if(bfs(i)){
			ans=i;
			break;
		}
	}
	printf("%d", ans);
	return 0;
}

方法3:宽搜+二分,A了,更快

#include <bits/stdc++.h>
using namespace std;
int n, m, x, y, nx, ny, l, r, mid;
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
int p[1010][1010], ans;
bool vis[1010][1010];
queue<int> qx, qy;
bool bfs(int asd)
{
	while(!qx.empty()){
		qx.pop();
		qy.pop();
	}
	memset(vis, 0, sizeof(vis));
	for(int i=1; i<=n; ++i){
		qx.push(1);
		qy.push(i);	
		vis[1][i]=true;
	}
	while(!qx.empty()){
		x=qx.front();
		y=qy.front();
		qx.pop();
		qy.pop();
		if(x==n){
			return true;
		}
		for(int i=1; i<=4; ++i){
			nx=x+mx[i];
			ny=y+my[i];
			if(nx>=1 && nx<=n && ny>=1 && ny<=m && p[nx][ny]<=asd && !vis[nx][ny]){
				vis[nx][ny]=true;
				qx.push(nx);
				qy.push(ny);	
			}
		}
	}
	return false;
}
int main()
{
	scanf("%d %d", &n, &m);
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=m; ++j){
			scanf("%d", &p[i][j]);
			r=max(r, p[i][j]);
		}
	}
	while(l<r){
		mid=(l+r)>>1;
		if(bfs(mid)){	//伤害为mid时, 可以到达, 尝试更小的伤害, 看左区间, 包含mid 
			r=mid; 
		}
		else{			//到达不了, 尝试更大的伤害, 看右区间, 不包含mid 
			l=mid+1; 
		}
	}
	ans=l;
	printf("%d", ans);
	return 0;
}

T203428 zhaojinxi的奥数作业

#include <bits/stdc++.h>
using namespace std;
int t, n, m, x, ans[10006];
bool vis[10006];
queue<int> q;
stack<int> qq;
//一分为二
void f1(int a)
{
	int asd=a;
	if(asd%2){
		asd=(asd+1)/2;
	}
	else{
		asd/=2;
	} 
	if(!vis[asd]){
		q.push(asd);
		vis[asd]=true;
		ans[asd]=ans[a]+1;
	}
}
//丢三落四
void f2(int a)
{
	int asd=a;
	while(asd){
		if(asd%10!=3 && asd%10!=4){
			qq.push(asd%10);
		}
		asd/=10;
	}
	while(!qq.empty()){
		asd=asd*10+qq.top();
		qq.pop();
	}
	if(!vis[asd]){
		q.push(asd);
		vis[asd]=true;
		ans[asd]=ans[a]+1;
	}
}
//七上八下
void f3(int a)
{
	int asd=a;
	int cnt7=0, cnt8=0;
	while(asd){
		if(asd%10==7){
			cnt7++;
		}
		else if(asd%10==8){
			cnt8++;
		}
		asd/=10;
	}
	asd=a;
	while(cnt8--){
		qq.push(8);
	}
	while(asd){
		if(asd%10!=7 && asd%10!=8){
			qq.push(asd%10);
		}
		asd/=10;
	}
	while(cnt7--){
		qq.push(7);
	}
	while(!qq.empty()){
		asd=asd*10+qq.top();
		qq.pop();
	}
	if(!vis[asd]){
		q.push(asd);
		vis[asd]=true;
		ans[asd]=ans[a]+1;
	}
}
//十全十美
void f4(int a)
{
	int asd=a;
	asd=asd/10*10;
	if(!vis[asd]){
		q.push(asd);
		vis[asd]=true;
		ans[asd]=ans[a]+1;
	}
}
void bfs()
{
	while(!q.empty()){
		q.pop();
	}
	memset(vis, 0, sizeof(vis));
	memset(ans, 0, sizeof(ans));
	q.push(n);
	vis[n]=true;
	while(!q.empty()){
		x=q.front();
		q.pop();
		if(x==m){
			printf("%d\n", ans[x]);
			return;
		}
		f1(x);
		f2(x);
		f3(x);
		f4(x);
	}
	printf("-1\n");
}
int main()
{
	scanf("%d", &t);
	while(t--){
		scanf("%d %d", &n, &m);	
		bfs();
	}
	return 0;
}

宣宣和学霸君的约会

题目背景

宣宣和学霸君在学信息学竞赛中建立了良好的友情。

题目描述

宣宣要和学霸君约会,宣宣站在一个棋盘的1,1点,学霸君站在n,m点,两个人见面心切,希望同时朝对方的方向前进,但是有一些嫉妒他们感情交好的人会阻止他们约会,因此会在中间制造障碍点,宣宣和学霸君都得绕开障碍点前进,输出他们之间的最短距离。每两个位置之间是1的距离。如果两个人无法见面,则输出“wo tai nan le”题目要求使用双向宽搜。

输入格式

第一行n,m
第二行n行,m列的01序列,两数字间有空格,0表示可行点,1表示障碍点. 题目保证1,1点和n,m点没有障碍。

输出格式

1,1到n,m最短路程

样例 #1

样例输入 #1
4 5
0 1 0 1 0
0 0 0 0 0
0 1 0 1 0
0 1 0 0 0
样例输出 #1

7

样例 #2

样例输入 #2
6 6
0 0 1 0 1 0
1 0 0 0 0 0
0 1 1 1 1 0
0 0 0 0 0 0
0 1 1 1 1 1
0 0 0 0 0 0
样例输出 #2
20

提示

n,m <=5000

宽搜

#include <bits/stdc++.h>
using namespace std;
int n, m;
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
int a[5010][5010], dis[5010][5010];
bool vis[5010][5010];
queue<int> qx, qy;
void bfs()
{
	int x, y, nx, ny;
	qx.push(1);
	qy.push(1);
	vis[1][1]=true;
	while(!qx.empty()){
		x=qx.front();
		y=qy.front();
		if(x==n && y==m){
			printf("%d", dis[x][y]);
			exit(0);
		}
		qx.pop();
		qy.pop();
		for(int i=1; i<=4; ++i){
			nx=x+mx[i];
			ny=y+my[i];
			if(nx>=1 && nx<=n && ny>=1 && ny<=m && !vis[nx][ny] && a[nx][ny]==0){
				qx.push(nx);
				qy.push(ny);
				dis[nx][ny]=dis[x][y]+1;
				vis[nx][ny]=true;
			}
		}
	}
}
int main()
{
	scanf("%d %d", &n, &m);
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=m; ++j){
			scanf("%d", &a[i][j]);
		}
	}
	bfs();
	printf("wo tai nan le");
	return 0;
}

双向宽搜

#include <bits/stdc++.h>
using namespace std;
int n, m;
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
int a[5010][5010], dis1[5010][5010], dis2[5010][5010];
bool vis1[5010][5010], vis2[5010][5010];
queue<int> q1x, q1y, q2x, q2y;
//双向宽搜 
void bfs()
{
	int x, y, xx, yy, nx, ny;
	//(1, 1)点入队 
	q1x.push(1);
	q1y.push(1);
	vis1[1][1]=true;
	//(n, m)点入队 
	q2x.push(n);
	q2y.push(m);
	vis2[n][m]=true;
	//队列不为空 
	while(!q1x.empty()){
		x=q1x.front();
		y=q1y.front();
		q1x.pop();
		q1y.pop();
		
		xx=q2x.front();
		yy=q2y.front();
		q2x.pop();
		q2y.pop();
		
		if(vis1[xx][yy]){
			printf("%d", dis1[xx][yy]+dis2[xx][yy]);
			exit(0);
		}
		if(vis2[x][y]){
			printf("%d", dis1[x][y]+dis2[x][y]);
			exit(0);
		}
		
		for(int i=1; i<=4; ++i){
			nx=x+mx[i];
			ny=y+my[i];
			if(nx>=1 && nx<=n && ny>=1 && ny<=m && !vis1[nx][ny] && a[nx][ny]==0){	
				q1x.push(nx);
				q1y.push(ny);
				dis1[nx][ny]=dis1[x][y]+1;
				vis1[nx][ny]=true;
			}
			
			nx=xx+mx[i];
			ny=yy+my[i];
			if(nx>=1 && nx<=n && ny>=1 && ny<=m && !vis2[nx][ny] && a[nx][ny]==0){		
				q2x.push(nx);
				q2y.push(ny);
				dis2[nx][ny]=dis2[xx][yy]+1;
				vis2[nx][ny]=true;
			}
		}
	}
}
int main()
{
	scanf("%d %d", &n, &m);
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=m; ++j){
			scanf("%d", &a[i][j]);
		}
	}
	bfs();
	printf("wo tai nan le");
	return 0;
}

P4667 [BalticOI 2011 Day1]Switch the Lamp On

deque

#include <bits/stdc++.h>
using namespace std;
int n, m, a[510][510], dis[510][510][2];
int mx[5]={0, -1, 1, 0, 0}, mx1[3]={0, -1, 1}, mx2[3]={0, 1, -1};	//上下左右; 左上右下; 左下右上 
int my[5]={0, 0, 0, -1, 1}, my1[3]={0, -1, 1}, my2[3]={0, -1, 1};
bool vis[510][510][2], in[510][510][2];
char ch;
map<char, int> mp;
struct node
{
	int qx, qy, qdir, dis;	//行、列、朝向
}asd; 
deque<node> q;	 
void bfs()
{
	int x, y, nx, ny, dir, ndir;
	while(!q.empty()){
		asd=q.front();
		x=asd.qx;
		y=asd.qy;
		dir=asd.qdir;
		q.pop_front();
		in[x][y][dir]=false;
		//上下左右 
		for(int i=1; i<=4; ++i){
			nx=x+mx[i];
			ny=y+my[i]; 
			if(nx>=1 && nx<=n && ny>=1 && ny<=m){
				ndir=a[nx][ny]; 
				if(dir!=ndir){	//不需要翻转, 可以直接联通 
					if(!vis[nx][ny][ndir] || dis[nx][ny][ndir]>dis[x][y][dir]){
						vis[nx][ny][ndir]=true;
						dis[nx][ny][ndir]=dis[x][y][dir];
						//不在队列中才入队 
						if(!in[nx][ny][ndir]){
							q.push_front({nx, ny, ndir, dis[nx][ny][ndir]});
							in[nx][ny][ndir]=true;
						}
					}
				}
				else if(dir==ndir){
					//需要翻转a[nx][ny], 才能联通 
					ndir=ndir^1;
					if(!vis[nx][ny][ndir] || dis[nx][ny][ndir]>dis[x][y][dir]+1){
						vis[nx][ny][ndir]=true;
						dis[nx][ny][ndir]=dis[x][y][dir]+1;
						//不在队列中才入队 
						if(!in[nx][ny][ndir]){
							q.push_back({nx, ny, ndir, dis[nx][ny][ndir]});
							in[nx][ny][ndir]=true;
						}
					}				
				}
			}
		}
		//左上右下 
		if(dir==0){
			for(int i=1; i<=2; ++i){
				nx=x+mx1[i];
				ny=y+my1[i];
				if(nx>=1 && nx<=n && ny>=1 && ny<=m){
					ndir=a[nx][ny];
					if(dir==ndir){	//不需要翻转, 可以直接联通 
						if(!vis[nx][ny][ndir] || dis[nx][ny][ndir]>dis[x][y][dir]){
							vis[nx][ny][ndir]=true;
							dis[nx][ny][ndir]=dis[x][y][dir];
							//不在队列中才入队 
							if(!in[nx][ny][ndir]){
								q.push_front({nx, ny, ndir, dis[nx][ny][ndir]});
								in[nx][ny][ndir]=true;
							}
						}
					}
					else if(dir!=ndir){
						//需要翻转a[nx][ny], 才能联通 
						ndir=ndir^1;
						if(!vis[nx][ny][ndir] || dis[nx][ny][ndir]>dis[x][y][dir]+1){
							vis[nx][ny][ndir]=true;
							dis[nx][ny][ndir]=dis[x][y][dir]+1;
							//不在队列中才入队 
							if(!in[nx][ny][ndir]){
								q.push_back({nx, ny, ndir, dis[nx][ny][ndir]});
								in[nx][ny][ndir]=true;
							}
						}				
					}
				}
			} 
		} 
		//左下右上 
		if(dir==1){
			for(int i=1; i<=2; ++i){
				nx=x+mx2[i];
				ny=y+my2[i];
				if(nx>=1 && nx<=n && ny>=1 && ny<=m){
					ndir=a[nx][ny];
					if(dir==ndir){	//不需要翻转, 可以直接联通 
						if(!vis[nx][ny][ndir] || dis[nx][ny][ndir]>dis[x][y][dir]){
							vis[nx][ny][ndir]=true;
							dis[nx][ny][ndir]=dis[x][y][dir];
							//不在队列中才入队 
							if(!in[nx][ny][ndir]){
								q.push_front({nx, ny, ndir, dis[nx][ny][ndir]});
								in[nx][ny][ndir]=true;
							}
						}
					}
					else if(dir!=ndir){
						//需要翻转a[nx][ny], 才能联通 
						ndir=ndir^1;
						if(!vis[nx][ny][ndir] || dis[nx][ny][ndir]>dis[x][y][dir]+1){
							vis[nx][ny][ndir]=true;
							dis[nx][ny][ndir]=dis[x][y][dir]+1;
							//不在队列中才入队 
							if(!in[nx][ny][ndir]){
								q.push_back({nx, ny, ndir, dis[nx][ny][ndir]});
								in[nx][ny][ndir]=true;
							}
						}				
					}
				}
			} 
		}  
	}
}
int main()
{
	ch='\\';
	mp[ch]=0;
	ch='/';
	mp[ch]=1;
	scanf("%d %d", &n, &m);		//n行m列
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=m; ++j){		
			cin >> ch;
			a[i][j]=mp[ch];
		}
	}
	memset(dis, 0x3f, sizeof(dis));
	dis[1][1][0]=0;
	//如果起点为'/', 需要把起点转换为'\'
	if(a[1][1]==1){
		dis[1][1][0]=1;
		vis[1][1][0]=true;
		a[1][1]=0;
	}
	vis[1][1][0]=true;
	vis[1][1][1]=true;
	q.push_front({1, 1, 0, dis[1][1][0]});	// 0表示'\' , 1表示'/' 
	in[1][1][0]=true;
	bfs();
	if(dis[n][m][0]==0x3f3f3f3f){
		printf("NO SOLUTION");
	}
	else{
		printf("%d\n", dis[n][m][0]);
	}
	return 0;
}

priority_queue

#include <bits/stdc++.h>
using namespace std;
int n, m, a[510][510], dis[510][510][2];
int mx[5]={0, -1, 1, 0, 0}, mx1[3]={0, -1, 1}, mx2[3]={0, 1, -1};	//上下左右; 左上右下; 左下右上 
int my[5]={0, 0, 0, -1, 1}, my1[3]={0, -1, 1}, my2[3]={0, -1, 1};
bool vis[510][510][2], in[510][510][2];
char ch;
map<char, int> mp;
struct node
{
	int qx, qy, qdir, dis;	//行、列、朝向
}asd; 
bool operator <(node x, node y)
{
	return x.dis>y.dis;
}
priority_queue<node> q;	 
void bfs()
{
	int x, y, nx, ny, dir, ndir;
	while(!q.empty()){
		asd=q.top();
		nx=x=asd.qx;
		ny=y=asd.qy;
		dir=asd.qdir;
		q.pop();
		in[x][y][dir]=false;
		//上下左右 
		for(int i=1; i<=4; ++i){
			nx=x+mx[i];
			ny=y+my[i]; 
			if(nx>=1 && nx<=n && ny>=1 && ny<=m){
				ndir=a[nx][ny]; 
				if(dir!=ndir){	//不需要翻转, 可以直接联通 
					if(!vis[nx][ny][ndir] || dis[nx][ny][ndir]>dis[x][y][dir]){
						vis[nx][ny][ndir]=true;
						dis[nx][ny][ndir]=dis[x][y][dir];
						//不在队列中才入队 
						if(!in[nx][ny][ndir]){
							q.push({nx, ny, ndir, dis[nx][ny][ndir]});
							in[nx][ny][ndir]=true;
						}
					}
				}
				else if(dir==ndir){
					//需要翻转a[nx][ny], 才能联通 
					ndir=ndir^1;
					if(!vis[nx][ny][ndir] || dis[nx][ny][ndir]>dis[x][y][dir]+1){
						vis[nx][ny][ndir]=true;
						dis[nx][ny][ndir]=dis[x][y][dir]+1;
						//不在队列中才入队 
						if(!in[nx][ny][ndir]){
							q.push({nx, ny, ndir, dis[nx][ny][ndir]});
							in[nx][ny][ndir]=true;
						}
					}				
				}
			}
		}
		//左上右下 
		if(dir==0){
			for(int i=1; i<=2; ++i){
				nx=x+mx1[i];
				ny=y+my1[i];
				if(nx>=1 && nx<=n && ny>=1 && ny<=m){
					ndir=a[nx][ny];
					if(dir==ndir){	//不需要翻转, 可以直接联通 
						if(!vis[nx][ny][ndir] || dis[nx][ny][ndir]>dis[x][y][dir]){
							vis[nx][ny][ndir]=true;
							dis[nx][ny][ndir]=dis[x][y][dir];
							//不在队列中才入队 
							if(!in[nx][ny][ndir]){
								q.push({nx, ny, ndir, dis[nx][ny][ndir]});
								in[nx][ny][ndir]=true;
							}
						}
					}
					else if(dir!=ndir){
						//需要翻转a[nx][ny], 才能联通 
						ndir=ndir^1;
						if(!vis[nx][ny][ndir] || dis[nx][ny][ndir]>dis[x][y][dir]+1){
							vis[nx][ny][ndir]=true;
							dis[nx][ny][ndir]=dis[x][y][dir]+1;
							//不在队列中才入队 
							if(!in[nx][ny][ndir]){
								q.push({nx, ny, ndir, dis[nx][ny][ndir]});
								in[nx][ny][ndir]=true;
							}
						}				
					}
				}
			} 
		} 
		//左下右上 
		if(dir==1){
			for(int i=1; i<=2; ++i){
				nx=x+mx2[i];
				ny=y+my2[i];
				if(nx>=1 && nx<=n && ny>=1 && ny<=m){
					ndir=a[nx][ny];
					if(dir==ndir){	//不需要翻转, 可以直接联通 
						if(!vis[nx][ny][ndir] || dis[nx][ny][ndir]>dis[x][y][dir]){
							vis[nx][ny][ndir]=true;
							dis[nx][ny][ndir]=dis[x][y][dir];
							//不在队列中才入队 
							if(!in[nx][ny][ndir]){
								q.push({nx, ny, ndir, dis[nx][ny][ndir]});
								in[nx][ny][ndir]=true;
							}
						}
					}
					else if(dir!=ndir){
						//需要翻转a[nx][ny], 才能联通 
						ndir=ndir^1;
						if(!vis[nx][ny][ndir] || dis[nx][ny][ndir]>dis[x][y][dir]+1){
							vis[nx][ny][ndir]=true;
							dis[nx][ny][ndir]=dis[x][y][dir]+1;
							//不在队列中才入队 
							if(!in[nx][ny][ndir]){
								q.push({nx, ny, ndir, dis[nx][ny][ndir]});
								in[nx][ny][ndir]=true;
							}
						}				
					}
				}
			} 
		}  
	}
}
int main()
{
	ch='\\';
	mp[ch]=0;
	ch='/';
	mp[ch]=1;
	scanf("%d %d", &n, &m);		//n行m列
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=m; ++j){		
			cin >> ch;
			a[i][j]=mp[ch];
		}
	}
	memset(dis, 0x3f, sizeof(dis));
	dis[1][1][0]=0;
	//如果起点为'/', 需要把起点转换为'\'
	if(a[1][1]==1){
		dis[1][1][0]=1;
		vis[1][1][0]=true;
		a[1][1]=0;
	}
	vis[1][1][0]=true;
	vis[1][1][1]=true;
	q.push({1, 1, 0, dis[1][1][0]});	// 0表示'\' , 1表示'/' 
	in[1][1][0]=true;
	bfs();
	if(dis[n][m][0]==0x3f3f3f3f){
		printf("NO SOLUTION");
	}
	else{
		printf("%d\n", dis[n][m][0]);
	}
	return 0;
}

P1032 [NOIP2002 提高组] 字串变换

双向宽搜

//TODO

P5195 [USACO05DEC]Knights of Ni S

//TODO

P1363 幻象迷宫

#include <bits/stdc++.h>
using namespace std;
int n, m, startx, starty;
int mx[]={0, -1, 1, 0, 0};
int my[]={0, 0, 0, -1, 1};
int has[1510][1510];
char a[1510][1510];
bool vis[1510][1510];
queue<int> qx, qy;
inline int cal(int x, int y)
{
	return 123*x+11*y;
}
inline string bfs()
{
	qx.push(startx);
	qy.push(starty);
	has[startx][starty]=cal(startx, starty);
	while(!qx.empty()){
		int curx=qx.front();
		int cury=qy.front();
		qx.pop();
		qy.pop();
		for(int i=1; i<=4; ++i){
			int nx=curx+mx[i];
			int ny=cury+my[i];
			int okx=(nx+1000*n)%n;
			int oky=(ny+1000*m)%m;
			if(a[okx][oky]=='#'){
				continue;
			}
			else if(!vis[okx][oky]){
				qx.push(nx);
				qy.push(ny);
				vis[okx][oky]=true;
				has[okx][oky]=cal(nx, ny);
			}
			else if(has[okx][oky]!=cal(nx, ny)){
				return "Yes"; 
			}
		}
	}
	return "No";
}
int main()
{
	while(scanf("%d %d", &n, &m)!=EOF){
		for(int i=0; i<=n-1; ++i){
			for(int j=0; j<=m-1; ++j){
				cin >> a[i][j];
				if(a[i][j]=='S'){
					startx=i;
					starty=j;
				}
			}
		}
		memset(vis, 0, sizeof(vis));
		memset(has, 0, sizeof(has));
		while(!qx.empty()){
			qx.pop();
			qy.pop();
		}
		cout << bfs() << endl; 
	}
	return 0;
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ypeijasd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值