目录
P1596 [USACO10OCT]Lake Counting S
P2895 [USACO08FEB]Meteor Shower S
P3663 [USACO17FEB]Why Did the Cow Cross the Road III S
P3110 [USACO14DEC]Piggy Back S
P1825 [USACO11OPEN]Corn Maze S
P4667 [BalticOI 2011 Day1]Switch the Lamp On
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;
}