专题训练——搜索专题

传送门
这套题我用的memset比较多,是因为这套题的数据量都比较小,想着省事 ,在实际做题中,如果时间限制短,或者多样例时,强烈不建议用memset,memset时间消耗太大。
A、Red and Black
题解:搜索模板题,dfs和bfs都可以,直接爆搜,能搜到 ’ . ’ 答案就加一
代码:

#include<bits/stdc++.h>

using namespace std;

int ans=0;

int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int n,m;
string s[22];

bool st[22][22];

void dfs(int u,int v){
	ans++;
	st[u][v]=true;
	for(int i=0;i<4;i++){
		int x=u+dx[i];
		int y=v+dy[i];
		if(x<0||y<0||x>=n||y>=m) continue;
		if(st[x][y]||s[x][y]=='#') continue;
		dfs(x,y);
	}
}

int main(){
	
	while(cin>>m>>n,n||m){
		memset(st,0,sizeof st);
		for(int i=0;i<n;i++)
		cin>>s[i];
		for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
		if(s[i][j]=='@') dfs(i,j);
		cout<<ans<<endl;
		ans=0;
	}
}

B、Oil Deposits
题解:直接遍历字符矩阵 遇到‘@’ 并且没被标记就爆搜,每次暴搜都把搜到的点标记,这样就保证了每次搜索就是搜索一个新的连通块,这样答案就是连通块的个数=爆搜的次数,注意这里是八方向:上、下、左、右、左上、左下、右上、右下;

代码:

#include<iostream>
#include<string.h>

using namespace std;

int n,m;

int ax[8]= {-1,-1,-1,0,0,1,1,1},ay[8]= {-1,0,1,-1,1,-1,0,1};//八方向 
int b[110][110];
char a[110][110];

void dfs(int u,int k)
{
    b[u][k]=1;
    if(u<0||u>=n||k<0||k>=m)
        return ;
    for(int i=0;i<8;i++){
        if(a[u+ax[i]][k+ay[i]]=='@'&&b[u+ax[i]][k+ay[i]]==0)
            dfs(u+ax[i],k+ay[i]);
    }
}

int main()
{
    //int n,m;
    while(scanf("%d%d",&n,&m)&&m!=0)
    {

        int t=0;
        memset(b,0,sizeof b);

        for(int i=0; i<n; i++)
            scanf("%s",&a[i]);
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<m; j++)
            {
                if(a[i][j]=='@'&&b[i][j]==0)//遇到@,并且之前没被搜过,这样就保证了 这次搜的是一个新的连通块 
                    dfs(i,j),t++;
            }
        }
        printf("%d\n",t);//答案就是连通块的个数=搜索的次数 
    }
}

C、Lake Counting
题解:思路同上一题,这里采用不同的搜索方式:即搜索八连通块的时候直接利用for循环遍历,把搜索过的点变成 ’ . ’ 。习惯采用那种都可以个人习惯采用B题代码
代码:

#include <iostream>
using namespace std;

int N,M;
char field[100][100];

void dfs(int x,int y){
	field[x][y]='.';
	
	for(int dx=-1;dx<=1;dx++){
		for(int dy=-1;dy<=1;dy++){
			int nx=x+dx,ny=y+dy;
			if(0<=nx&&nx<N&&0<=ny&&ny<M&&field[nx][ny]=='W') dfs(nx,ny);
		}
	}
}

int main()
{
	int res=0;
	int i,j;
	scanf("%d %d",&N,&M);
	for(i=0;i<N;i++){
		for(j=0;j<M;j++){
			cin>>field[i][j];
		}
	}
	for(i=0;i<N;i++){
		for(j=0;j<M;j++){
			if(field[i][j]=='W'){
				dfs(i,j);
				res++;
			}
		}
	}
	printf("%d",res);
	return 0;
}

D、N皇后问题
题解:经典 n皇后问题,注意不要每次都搜,先预处理。
代码:

#include <iostream>

using namespace std;

bool st1[52], st2[52], st3[52];
int n, t, ans, a[20];

void dfs(int k) {
    if(k == n) {
        t++;
        return;

    }
    for(int i = 0; i < n; i++) {
        if(!st1[i] && !st2[i + k] && !st3[n - k + i] ) {
            //a[k][i] = 1;
            st1[i] = st2[i + k] = st3[n - k + i] = true;

            dfs(k + 1);
            // a[k][i] = 0;
            st1[i] = st2[i + k] = st3[n - k + i] = false;
        }
    }


}

int main() {
    for(int i = 1; i <= 10; i++) {
        t = 0;
        n = i;
        dfs(0);
        a[i] = t;
    }
    while(cin >> n && n != 0) {
        // for(int i = 0; i < n; i++) {
        //   for(int j = 0; j < n; j++)
        //     a[i][j] = 0;
        //}
        //t = 0, ans = 0;
        //for(int i = 0; i <= 22; i++)
        //  st1[i] =false, st2[i] =false, st3[i] = false;

        //dfs(0);
        cout << a[n] << endl;

    }

}

E、排列2
题解:经典全排列问题,但是要注意可能有相同的数,所以要去重一下,还有就是0不能作为千位,最后注意处理一下输出格式,细节见代码。
代码:

#include<iostream>
#include<algorithm>
#include<vector>
#include<string.h>

using namespace std;

vector<int>p;
int b[10010];

int st[10010];

int c[30],cnt,sum;
int a[4];

void dfs(int k) {//A^A^B^C^C    A^A^C^C^B
	if(k==4) {//搜完了四张牌 
		if(b[sum]==0) {
			c[cnt++]=sum;//c数组存最后的答案 
			b[sum]++;//b数组作为去重数组,表示sum已经出现过 
		}
		return ;
	}
	for(int i=0; i<4; i++) {
		if(!st[i]&&!(sum==0&&a[i]==0)) {//st数组表示第i张牌是否用过st[i]==0表示未被使用,!(sum==0&&a[i]==0)表示0不能作为最高位 
			st[i]++;
			sum=sum*10+a[i];
			dfs(k+1);
			sum=sum/10;//这种情况已经搜完,要恢复现场 
			st[i]--;
		}
	}
}

int main() {
	int xx=0;
	while(1) {
		if(xx==1)
			cout<<"\n";
		cnt=0,sum=0;
		memset(st,0,sizeof st);
		memset(b,0,sizeof b);
		memset(c,0,sizeof c);
		//int a[4];
		cin>>a[0]>>a[1]>>a[2]>>a[3];
		if(a[0]==a[1]&&a[1]==a[2]&&a[2]==a[3]&&a[0]==0)
			break;
		if(!(a[0]==a[1]&&a[1]==a[2]&&a[2]==a[3]&&a[0]==0)&&xx==1)
			cout<<"\n";
		xx=1;
		memset(b,0,sizeof b);
		dfs(0);
		sort(c,c+cnt+1);
		for(int i=1; i<=cnt; i++) {
			cout<<c[i];
			if(c[i]/1000!=c[i+1]/1000&&i<cnt)
				cout<<endl;
			else if(c[i]/1000==c[i+1]/1000)
				cout<<" ";
		}
	}
}


F、Catch That Cow
题解:直接从给出的初始的数开始暴搜,每次转移可以转移到三种状态并且搜索的最大值不超过2e5,这里简单证明一下:因为N和K值域都是[0,1e5],所以无论给出的N和K取值多少,就算都用N++或者N-- 答案肯定不超过1e5。但是如果搜索时N超过了2e5,这个时候就只能用N–,就算是K=1e5,也要1e5步。综上,搜索时N的值肯定不会超过2e5,可以用上面的证明方式可以证明出来N也不会小于0。因为bfs搜索的时候具有最近性(求最短路时也可以表现出来),直接用bfs暴搜;
我这里用了一个小trick,因为要标记某个数是否之前被搜过,普通做法是开一个标记,我这里把 f[n]初始成1,只要 f [ i ] 不等于0,就表示之前被搜过,因为 n 这个数也要表示为被搜过所以要初始成 1 ,最后输出答案时记得 -1
代码:

#include<bits/stdc++.h>

using namespace std;

int f[200010];

int main() {
	ios::sync_with_stdio(false);

	int n,k;
	while(cin>>n>>k) {
		memset(f,0,sizeof f);
		queue<int>q;
		q.push(n);
		f[n]=1;
		while(!q.empty()) {
			int t=q.front();
			q.pop();
			if(t+1<=2e5&&!f[t+1]) {
				f[t+1]=f[t]+1;
				q.push(t+1);
			}
			if(t-1>=0&&!f[t-1]) {
				f[t-1]=f[t]+1;
				q.push(t-1);
			}
			if(t*2<=2e5&&!f[t*2]) {
				f[t*2]=f[t]+1;
				q.push(t*2);
			}
		}
		cout<<f[k]-1<<"\n";
		
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值