其他分享
首页 > 其他分享> > [周专题系列]kuangbin 专题1 简单搜索

[周专题系列]kuangbin 专题1 简单搜索

作者:互联网

[周专题系列]kuangbin 专题1 简单搜索

题单链接

知识点

题目类型
棋盘问题dfs回溯八皇后变形
Dungeon Master3D BFS求最短路
Catch That Cow一维BFS最短路,估计搜索范围,BFS缩小数据范围
Fliptile选与不选,二进制枚举,输出字典序最小
Find The MultipleBFS找最小的,缩小范围
Prime PathBFS搜索缩小答案范围直接输出
Shuffle’m Up模拟
POTSBFS利用string记录最短路径
Fire Game多源点BFS
Fire!两个物体的BFS
迷宫问题BFS记录路径
Oil DepositsFLood-Fill
非常可乐模拟/数论
Find a Way两次BFS

题目

棋盘问题

思路
八皇后变形,区别是不用每行都放满,其他差不多
每一行有选和不选两种,然后枚举选第几列
dfs状态参数(对第几行作选择,已经放了多少个)
不需要把列也作为参数,col数组已经记录了
代码

#include<bits/stdc++.h>
using namespace std;
const int N=10;
char g[N][N];
bool col[N];
int n,k,ans;
void dfs(int x,int t)
{
    if(t==k)
    {
        ans++;
        return;
    }
    if(x==n)return;
    for(int i=0;i<n;i++)
    {
        if(col[i]||g[x][i]!='#')continue;
        col[i]=1;
        dfs(x+1,t+1);   
        col[i]=0;
    }
    dfs(x+1,t);
}
int main()
{
    while(~scanf("%d%d",&n,&k))
    {
        if(n==-1&&k==-1)break;
        for(int i=0;i<n;i++)scanf("%s",g[i]);
        ans=0;
        dfs(0,0);
        printf("%d\n",ans);
    }
    return 0;
}

Dungeon Master

思路
3维度BFS最短路,比较容易。
需要注意的是,POJ C++98标准还不支持结构体{},交G++就可以了
代码

#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int N=35;
int L,R,C;
int sx,sy,sz,ex,ey,ez;
bool flag;
char g[N][N][N];
bool st[N][N][N];
int dist[N][N][N];
struct Node
{
    int z,x,y;
};
int dx[]={1,-1,0,0,0,0};
int dy[]={0,0,1,-1,0,0};
int dz[]={0,0,0,0,1,-1};
void bfs(int sz,int sx,int sy)
{
    memset(dist,0x3f,sizeof dist);
    Node input;
    input.z=sz,input.x=sx,input.y=sy;
    queue<Node>q;
    q.push(input);
    st[sz][sx][sy]=1;
    dist[sz][sx][sy]=0;
    while(q.size())
    {
        Node t=q.front();
        if(t.z==ez&&t.x==ex&&t.y==ey)
        {
            flag=1;
            break;
        }
        q.pop();
        for(int i=0;i<6;i++)
        {
            int a=t.z+dz[i];
            int b=t.x+dx[i];
            int c=t.y+dy[i];
            if(a<0||b<0||c<0||a>=L||b>=R||c>=C)continue;
            if(st[a][b][c]||g[a][b][c]=='#')continue;
            st[a][b][c]=1;
            Node tmp;
            tmp.z=a,tmp.x=b,tmp.y=c;
            q.push(tmp);
            dist[a][b][c]=dist[t.z][t.x][t.y]+1;
        }
    }
}
int main()
{
    while(~scanf("%d%d%d",&L,&R,&C)&&(L||R||C))
    {
        for(int i=0;i<L;i++)
            for(int j=0;j<R;j++)
                scanf("%s",g[i][j]);
        
     
        for(int i=0;i<L;i++)
            for(int j=0;j<R;j++)
                for(int k=0;k<C;k++)
                {
                    if(g[i][j][k]=='S')sz=i,sx=j,sy=k;
                    if(g[i][j][k]=='E')ez=i,ex=j,ey=k;
                }
        memset(st,0,sizeof st);
        flag=0;
        bfs(sz,sx,sy);
        if(flag) printf("Escaped in %d minute(s).\n",dist[ez][ex][ey]);
        else puts("Trapped!");
    }
    return 0;
}

Catch That Cow

思路
acwing题单里刷到过了
抓住那头牛
代码
直接看那篇博客里就行,不过POJ交的话要把万能头改一下

Fliptile

思路
选与不选(二进制枚举),类似题目是蓝书上费解的开关
有以下性质:
1:每个点最多点一次
2:如果第一行已经固定,那么满足题意的点击方案最多只有一种。因为当第i行某位为1时,若前i行已经被固定,那么只能点击第i+1行该位置上的数字才能使第i行变为0,归纳后可以得到此结论(所以我们只要枚举第一行方案即可)
3:点击的先后顺序不影响结果(做搜索的时候要确定操作选择顺序是否会影响答案)

这题要求最小操作数并输出字典序最小的,那么我们可以把输出的二维数组压缩为string字符串,如果当前方案数量和已有最小数量相等比较ans和当前方案的字符串并更新

代码

#include<algorithm>
#include<iostream>
#include<string>
using namespace std;

const int N=20;
bool g[N][N],tmp[N][N];
int cnt[N][N];
string ans;
int n,m;
void change(int x,int y)//翻转模块
{
    tmp[x][y]=!tmp[x][y];
    if(x-1>=0)tmp[x-1][y]=!tmp[x-1][y];
    if(x+1<n)tmp[x+1][y]=!tmp[x+1][y];
    if(y-1>=0)tmp[x][y-1]=!tmp[x][y-1];
    if(y+1<m)tmp[x][y+1]=!tmp[x][y+1];
}
void init()//初始化
{
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
            tmp[i][j]=g[i][j];
}

bool check()//检查是否可以
{
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
            if(tmp[i][j])return 0;
    return 1;
}

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        ans="";
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++)
            {
                scanf("%d",&g[i][j]);
                tmp[i][j]=g[i][j];
            }
        int min_count=0x3f3f3f3f;
        for(int i=0;i<(1<<m);i++)//枚举第一行方案
        {
            init();
            int count=0;
            string str="";
            for(int j=0;j<m;j++)
                if(i&(1<<j))
                {
                    change(0,j);
                    str+='1';
                    count++;
                }
                else str+='0';
            for(int j=1;j<n;j++)
                for(int k=0;k<m;k++)
                    if(tmp[j-1][k])
                    {
                        change(j,k);
                        str+='1';
                        count++;
                    }
                    else str+='0';
            if(check())//更新答案
            {
                if(count<min_count)
                {
                    min_count=count;
                    ans=str;
                }
                else if(count==min_count)//字典序比较更新
                {
                    if(str<ans)
                    {
                        ans=str;
                        printf("new ans is ID=%d\n",i);
                    }
                }
            }
        }
        if(min_count!=0x3f3f3f3f)//输出答案
        {
            for(int i=0;i<n;i++)
            {
                for(int j=0;j<m;j++)
                {
                    printf("%c ",ans[i*m+j]);
                }
                puts("");
            }
        }
        else puts("IMPOSSIBLE");
    }
    return 0;
}

Find The Multiple

思路
找一个>=n的倍数m,且只由0,1构成,bfs搜索然后迷之MLE
看别人是c++ stl queue TLE
索性直接打表

原本有顾虑担心会超过long long,但是bfs不会,很小的。所以这种要大胆搞啊!说不定范围很小
代码
打表代码

#include<iostream>
#include<queue>
using namespace std;
typedef long long ll;
ll n;
queue<ll>q;
ll bfs()
{
    q.push(1);
    while(!q.empty())
    {
        ll t=q.front();
        q.pop();
        if(t%n==0&&t>=n)return t;
        q.push(t*10);
        q.push(t*10+1);
    }
    return q.front();
}
int main()
{
    while(~scanf("%lld",&n)&&n)
    {
        printf("%lld\n",bfs());
    }
    return 0;
}

Prime Path

思路
A,B为四位数
找到A-B的素数路径,每次换掉四位数中一个数并保证一直是素数。首位不能为0。找出最短路径
线性筛预处理,BFS求解即可

代码

#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>

using namespace  std;

const int N=1e5+10;
int prime[N],cnt;
bool st[N],used[N];
struct Node
{
    int x,s;
};
void get_prime(int n)
{
    st[0]=st[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(!st[i])prime[++cnt]=i;
        for(int j=1;prime[j]<=n/i;j++)
        {
            st[i*prime[j]]=1;
            if(i%prime[j]==0)break;
        }
    }
}

int find_path(int a,int b)
{
    Node tmp;
    tmp.x=a,tmp.s=0;
    queue<Node>q;
    q.push(tmp);
    used[a]=1;
    while(!q.empty())
    {
        Node t=q.front();
        q.pop();
        if(t.x==b)return t.s;
        for(int i=0;i<=9;i++)
        {
            int x1=t.x-t.x/1000*1000+1000*i;//1
            int x2=t.x-(t.x/100%10)*100+100*i;//2
            int x3=t.x-(t.x/10%10)*10+10*i;//3
            int x4=t.x-t.x%10+i;//4
            if(x1>=1000&&!st[x1]&&!used[x1])
            {
                tmp.x=x1;tmp.s=t.s+1;
                q.push(tmp);
                used[x1]=1;
            }
            if(!st[x2]&&!used[x2])
            {
                tmp.x=x2;tmp.s=t.s+1;
                q.push(tmp);
                used[x2]=1;
            }
            if(!st[x3]&&!used[x3])
            {
                tmp.x=x3;tmp.s=t.s+1;
                q.push(tmp);
                used[x3]=1;
            }
            if(!st[x4]&&!used[x4])
            {
                tmp.x=x4;tmp.s=t.s+1;
                q.push(tmp);
                used[x4]=1;
            }
        }
    }
    return -1;

}
int main()
{
    get_prime(N);
    int T,a,b;
    scanf("%d",&T);
    while(T--)
    {
        memset(used,0,sizeof(used));
        scanf("%d%d",&a,&b);
        int ans=find_path(a,b);
        if(ans==-1)puts("Impossible");
        else printf("%d\n",ans);
    }
    return 0;
}

Shuffle’m Up

思路
暴力模拟就完事了
代码

#include<iostream>
#include<algorithm>
#include<queue>
#include<string>
#include<map>

using namespace std;

map<string,int>mp;
int T,n;

int bfs(string a,string b,string c)
{
	queue<string>q;
	string A,B,C;
	int cnt=0;
	q.push(a);q.push(b);
	while(!q.empty())
	{
		if(q.size()==1)
		{
			C=q.front();
			if(C==c)return cnt;
			A=B="";
			q.pop();
			for(int i=0;i<n;i++)A+=C[i];
			for(int i=0;i<n;i++)B+=C[i+n];
			q.push(A);q.push(B);
		}
		else if(q.size()==2)
		{
			A=q.front();q.pop();
			B=q.front();q.pop();
			C="";
			for(int i=0;i<n;i++)C=C+B[i]+A[i];
			if(mp.count(C)==0)
			{
				mp[C]++;
				q.push(C);
				cnt++;
			}
			else return -1;
		}
	}
	return -1;
}
int main()
{
	scanf("%d",&T);
	for(int i=1;i<=T;i++)
	{
		string a,b,c;
		scanf("%d",&n);
		cin>>a>>b>>c;
		mp.clear();
		printf("%d %d\n",i,bfs(a,b,c));
	}
	return 0;
}

Pots

思路
模块化编程&广搜记忆路径
我利用string来记录感觉比较方便
代码

#include<iostream>
#include<string>
#include<cstring>
#include<queue>
#include<algorithm>
#include<stdio.h>
using namespace std;
const int N=1e6+10;
string Path[]={"FILL(1)","FILL(2)","POUR(1,2)","POUR(2,1)","DROP(1)","DROP(2)"};
struct Node
{
	int a,b,s;
	string p;
};
int A,B,C;
queue<Node>q;
bool st[N];
void F(int x,Node t)
{
	if(x==1&&!st[A*1000+t.b])q.push({A,t.b,t.s+1,t.p+'0'}),st[A*1000+t.b]=1;
	else if(x==2&&!st[t.a*1000+B]) q.push({t.a,B,t.s+1,t.p+'1'}),st[t.a*1000+B]=1;
}
void P(int x,int y,Node t)
{
	if(x==1&&y==2)
	{
		if(t.a+t.b>=B&&!st[(t.a+t.b-B)*1000+B])q.push({t.a+t.b-B,B,t.s+1,t.p+'2'}),st[(t.a+t.b-B)*1000+B]=1;
		else if(t.a+t.b<B&&!st[(t.a+t.b)]) q.push({0,t.a+t.b,t.s+1,t.p+'2'}),st[(t.a+t.b)]=1;
	}
	else
	{
		if(t.a+t.b>=A&&!st[A*1000+t.a+t.b-A])q.push({A,t.a+t.b-A,t.s+1,t.p+'3'}),st[A*1000+t.a+t.b-A]=1;
		else if(t.a+t.b<A&&!st[(t.a+t.b)*1000])q.push({t.a+t.b,0,t.s+1,t.p+'3'}),st[(t.a+t.b)*1000]=1;
	}
	
	
}
void D(int x,Node t)
{	
	if(x==1&&!st[t.b])q.push({0,t.b,t.s+1,t.p+'4'}),st[t.b]=1;
	else if(x==2&&!st[t.a*1000]) q.push({t.a,0,t.s+1,t.p+'5'}),st[t.a*1000]=1;
}

void bfs()
{
	q.push({0,0,0,""});
	st[0]=1;
	while(q.size())
	{
		Node t=q.front();
		if(t.a==C||t.b==C)
		{
			printf("%d\n",t.s);
			string ans=t.p;
			for(int i=0;i<t.s;i++)cout<<Path[ans[i]-'0']<<endl;
			return;
		}
		q.pop();
		F(1,t);F(2,t);
		P(1,2,t);P(2,1,t);
		D(1,t);D(2,t);
	}
	printf("impossible");
	return;
}

int main()
{
	cin>>A>>B>>C;
	bfs();
	return 0;
}

Fire Game

思路
双起点,四重循环枚举起点
代码

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

const int N=12,INF=0x3f3f3f3f;
char g[N][N];
int dist[N][N];
int st[N][N];
int n,m,T;
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
struct Node
{
	int x,y;
};
int bfs(int x1,int y1,int x2,int y2)
{
	queue<Node>q;
    Node tmp;
	for(int i=0;i<n;i++)//初始化
		for(int j=0;j<m;j++)
			dist[i][j]=INF;
	tmp.x=x1;tmp.y=y1;q.push(tmp);
    tmp.x=x2;tmp.y=y2;q.push(tmp);//插入两个起点
	dist[x1][y1]=dist[x2][y2]=0;
	st[x1][y1]++;st[x2][y2]++;
	while(q.size())
	{
		Node t=q.front();
		q.pop();
		for(int i=0;i<4;i++)
		{
			int a=t.x+dx[i],b=t.y+dy[i];
			if(a<0||b<0||a>=n||b>=m)continue;
			if(st[a][b]==2||g[a][b]!='#')continue;
            tmp.x=a;tmp.y=b;q.push(tmp);
			st[a][b]++;
			dist[a][b]=min(dist[a][b],dist[t.x][t.y]+1);
		}
	}
	int res=0;
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
			if(g[i][j]=='#')
			{
				if(dist[i][j]==0x3f3f3f3f)return -1;
				res=max(res,dist[i][j]);
			}
	return res;
}
int main()
{
	cin>>T;
	for(int C=1;C<=T;C++)
	{
		cin>>n>>m;
		for(int i=0;i<n;i++)scanf("%s",g[i]);
		int ans=INF;
		for(int i=0;i<n;i++)
			for(int j=0;j<m;j++)
				for(int p=0;p<n;p++)
					for(int q=0;q<m;q++)//枚举双起点
						if(g[i][j]=='#'&&g[p][q]=='#')
						{
							memset(st,0,sizeof(st));
							int d=bfs(i,j,p,q);
							if(d!=-1)ans=min(ans,d);
						}

		if(ans==INF)ans=-1;
		cout<<"Case "<<C<<": "<<ans<<endl;
	}
	return 0;
}

Fire

思路
两种操作
一种先把Fire全部蔓延预处理出每个点到火的最短距离(注意有多个火的源点)然后再让Joe BFS一遍,必须距离小于火到这里的距离才可以(然后我写裂开了呜呜呜)
另外一种思路是同时走,同一秒内人先走然后火再蔓延(可以用Flood-fill把蔓延的变成障碍)
下面是其他大佬的代码:uva Fire
代码

#include <cstdio>
#include <iostream>
#include <queue> 
#include <cstring>
using namespace std;
const int N = 1005;
int t, n, m, vis[N][N];
char g[N][N];
int dx[4] = {1, 0, 0, -1};
int dy[4] = {0, 1, -1, 0};
struct node {
	int x, y;
	node(int x, int y): x(x), y(y){}
}; 
void bfs() {
	queue<node> jq; //J所在的点的队列 
	queue<node> fq; //着火点的队列 
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			if (g[i][j] == 'J') {
				jq.push(node(i, j));
				vis[i][j] = 1;				
			} else if (g[i][j] == 'F') {
				fq.push(node(i, j));
				g[i][j] = '#';//代表不可走 
			}
		} 
	} 	
	int step = 0; 
	int cnt= 0;
	while (jq.size()) {
		step++; //每秒中更新队列里面的所有点
		//J先跑 火再蔓延 
		for (int i = 0, j = jq.size(); i < j; i++) {
			node t = jq.front();
			jq.pop();
			//表明这个点被火给蔓延了不能走了  因为是人先走,火再蔓延 
			if (g[t.x][t.y] == '#') continue; 
			for (int k = 0; k < 4; k++) {
				int fx = t.x + dx[k];
				int fy = t.y + dy[k];
				if (fx >= 0 && fy >= 0 && fx < n &&fy < m) {
					//表明下个点可走入队 
					if (g[fx][fy] != '#' && !vis[fx][fy]) {
						jq.push(node(fx, fy));
						vis[fx][fy] = 1;
					}
				} else {
					//代表走出了边界
					printf("%d\n", step);
					return; 
				}
			} 
		}
		//火蔓延
		for (int i = 0, j = fq.size(); i < j; i++) {
			node t = fq.front();
			fq.pop();
			for (int k = 0; k < 4; k++) {
				int fx = t.x + dx[k];
				int fy = t.y + dy[k];
				if (fx >= 0 && fy >= 0 && fx < n && fy < m && g[fx][fy] != '#') {
					fq.push(node(fx, fy));
					g[fx][fy] = '#';
				}
			} 
		}		
	}
	printf("IMPOSSIBLE\n");
}

int main() {
	scanf("%d", &t);
	while (t--) {
		scanf("%d%d", &n, &m);
		for (int i = 0; i < n; i++) {
			scanf("%s", g[i]);
		}
 		bfs();
 		memset(vis, 0, sizeof vis);
	}
 	return 0;
} 

迷宫问题

思路

代码

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

const int N=5;
int g[N][N];
struct Node
{
	int x,y;
	string s;
};
bool st[N][N];
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
void bfs()
{
	queue<Node>q;
	q.push({0,0,"00"});
	st[0][0]=1;
	while(q.size())
	{
		Node t=q.front();
		q.pop();
		if(t.x==4&&t.y==4)
		{
			int len=t.s.size();
			for(int i=0;i<len;i=i+2)
			{
				printf("(%c, %c)\n",t.s[i],t.s[i+1]);
			}
			return;
		}
		for(int i=0;i<4;i++)
		{
			int a=t.x+dx[i],b=t.y+dy[i];
			if(a<0||b<0||a>=5||b>=5)continue;
			if(g[a][b]==1||st[a][b])continue;
			char c='0'+a;
			string p=t.s+c;
			c='0'+b;p+=c;
			q.push({a,b,p});
			st[a][b]=1;
		}
	}
}
int main()
{
	for(int i=0;i<5;i++)
		for(int j=0;j<5;j++)
			scanf("%d",&g[i][j]);
	bfs();

	return 0;
}

Oil Deposits

思路
Flood-FILL算法
代码

#include<queue>
#include<iostream>
#include<string>
#include<cstring>

using namespace std;

const int N=105;
char g[N][N];
int n,m;
int dx[]={1,1,1,-1,-1,-1,0,0};
int dy[]={0,1,-1,0,1,-1,1,-1};
void dfs(int x,int y)
{
	for(int i=0;i<8;i++)
	{
		int a=x+dx[i],b=y+dy[i];
		if(a<0||b<0||a>=n||b>=m||g[a][b]!='@')continue;
		g[a][b]='*';
		dfs(a,b);
	}

	return;
}
int main()
{
	while(~scanf("%d%d",&n,&m)&&(n||m))
	{
		for(int i=0;i<n;i++)scanf("%s",&g[i]);
		int cnt=0;
		for(int i=0;i<n;i++)
			for(int j=0;j<m;j++)
				if(g[i][j]=='@')
				{
					g[i][j]='*';
					dfs(i,j);
					cnt++;
				}
		cout<<cnt<<endl;
	}

	return 0;
}

非常可乐

思路
BFS加模拟和原本POTS基本差不多但是看到别的人说可以找规律那就换一种写法(其实是懒得写模拟hhh)
很nb的证明

代码

#include<iostream>
using namespace std;
int a,b,c;
int gcd(int a,int b)
{
	return b?gcd(b,a%b):a;
}
int main()
{
	while(~scanf("%d%d%d",&a,&b,&c)&&(a||b||c))
	{
		a/=gcd(b,c);
		if(a&1)puts("NO");
		else printf("%d\n",a-1);
	}
	return 0;
}

Find a way

思路
两次BFS即可,注意要都能访问到的才更新答案
代码

#include<iostream>
#include<queue>
#include<cstring>
#include<stdio.h>
using namespace std;
const int N=205;
char g[N][N];
bool st[N][N];
int vis[N][N];
int dist[N][N];
int n,m;
struct Node
{
    int x,y;
};
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
void bfs(int sx,int sy)
{
    queue<Node>q;
    memset(st,0,sizeof(st));
    q.push({sx,sy});
    st[sx][sy]=1;
    vis[sx][sy]++;
    while(q.size())
    {
        Node t=q.front();
        q.pop();
        for(int i=0;i<4;i++)
        {
            int a=t.x+dx[i],b=t.y+dy[i];
            if(a<0||b<0||a>=n||b>=m||st[a][b]||g[a][b]=='#')continue;
            q.push({a,b});
            dist[a][b]+=dist[t.x][t.y]+11;
            vis[a][b]++;
            st[a][b]=1;
        }
    }
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
            if(g[i][j]!='@')dist[i][j]=0;

}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        memset(dist,0,sizeof(dist));
        memset(vis,0,sizeof(vis));
        for(int i=0;i<n;i++)scanf("%s",g[i]);
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++)
                if(g[i][j]=='Y'||g[i][j]=='M')
                    bfs(i,j);
        int ans=0x3f3f3f3f;
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++)
                if(g[i][j]=='@'&&vis[i][j]==2)ans=min(ans,dist[i][j]);
        printf("%d\n",ans);
    }
    return 0;
}

总结

BFS主要这几个题型:

  1. 最短路+模拟
  2. Flood-Fill
  3. 最短路缩小答案范围
  4. 多源点BFS
  5. 多物体BFS

标签:tmp,专题,int,st,搜索,&&,kuangbin,push,include
来源: https://blog.csdn.net/qq_39354847/article/details/114378244