暑期训练第二周专项练习:搜索与搜索剪枝

A老子的全排列呢

实际上考的就是简单的dfs,当每一个点都遍历过一遍的时候,就将这个序列输出,并回溯(将数组复原),进行下一次遍历。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e15 + 7;
int a[10];
int ss[10];
int n;
void dfs(int k){
    if(k>n){
        for(int i=1;i<=n;i++){
            cout<<a[i]<<" ";
        }
        cout<<endl;
    }
    for(int i=1;i<=n;i++){
        if(ss[i]==0){
            a[k]=i;
            ss[i]=1;
            dfs(k+1);
            ss[i]=0;
        }
    }
}
signed main(){
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    n=8;
    dfs(1);
}
B走出迷宫

我用是的dfs去写,问过队友后发现实际上也可以使用bfs去写。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e15 + 7;
int n,m;
int a,b,cnt=0;
char sum[505][505];
bool vis[505][505];
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
void dfs(int x,int y){
    if(sum[x][y]=='E'){
        cnt=1;
        return ;
    }
    for(int i=0;i<4;i++){
        int p=x+dx[i];
        int q=y+dy[i];
        if(p>=1&&p<=n&&q>=1&&q<=m&&vis[p][q]&&sum[p][q]!='#'){
            vis[p][q]=false;
            dfs(p,q);
            //vis[p][q]=true;
            //这道题不需要回溯,因为当前他只要找能不能走到出口,不用管从那个方向走到的出口,也不需要去管走了多少次才到终点。如果加上回溯的话,会超时。开始交了好多遍都没有找到问题,后来队友一眼就找到了。
        }
    }
}
signed main(){
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    while(cin>>n>>m){
        cnt=0;
        memset(vis,true,sizeof(vis));
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                cin>>sum[i][j];
                if(sum[i][j]=='S'){
                    a=i;
                    b=j;
                    vis[i][j]=false;
                }
            }
        }
        /*for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                cout<<sum[i][j]<<' ';
            }
            cout<<endl;
        }*/
        dfs(a,b);
        if(cnt) cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
}
C模拟战役

大炮爆炸是一个连锁反应,实际上在一个大炮被击毁的时候,周围3x3的网格内的如果有大炮,也会被击毁,在引起一个3x3的网格内产生的大炮爆炸,依次类推。所以是一连通块的问题,实际上就是每次都用齐齐的大炮数量最小的连通块去炸司机大炮数量最大的连通块,以此类推,所以可知只要齐齐的连通块数量小于司机的时候,那么此时齐齐就做不到将司机的大炮全部炸毁,就会输出-1;反之,是需要让齐齐的连通块进行排序,然后只要比司机多的那一部分的连通块内的大炮数量即可。至于求一个连通块内究竟有多少个大炮的话,用dfs去求。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e15 + 7;
int n;
char sum[7][105];
char ans[7][105];
vector<int> va,vb;
int dx[]={0,0,1,1,1,-1,-1,-1};
int dy[]={1,-1,-1,0,1,-1,0,1};
bool a[7][105],b[7][105];
int dfs1(int x,int y,int z){
    z++;
    a[x][y]=false;
    for(int i=0;i<8;i++){
        int xx=x+dx[i];
        int yy=y+dy[i];
        if(xx<1||xx>4||yy<1||yy>n) continue;//该点超出了所在范围
        if(!a[xx][yy]) continue;//该点已经被统计过了
        if(sum[xx][yy]=='.') continue;//该点不是大炮的时候
        z=dfs1(xx,yy,z);
    }
    return z;
}
int dfs2(int x,int y,int z){//同理,只不过dfs1统计的是司机的,dfs2统计的是齐齐的
    z++;
    b[x][y]=false;
    for(int i=0;i<8;i++){
        int xx=x+dx[i];
        int yy=y+dy[i];
        if(xx<1||xx>4||yy<1||yy>n) continue;
        if(!b[xx][yy]) continue;
        if(ans[xx][yy]=='.') continue;
        dfs2(xx,yy,z);
    }
    return z;
}
signed main(){
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    cin>>n;
//为了防止后边统计的时候比较麻烦,所以直接将齐齐的跟司机的分开存。
    for(int i=1;i<=4;i++){
        for(int j=1;j<=n;j++){
            cin>>sum[i][j];
        }
    }
    for(int i=1;i<=4;i++){
        for(int j=1;j<=n;j++){
            cin>>ans[i][j];
        }
    }
    memset(a,true,sizeof(a));
    memset(b,true,sizeof(b));
    for(int i=1;i<=4;i++){
        for(int j=1;j<=n;j++){
            if(a[i][j]&&sum[i][j]=='*'){
                va.push_back(dfs1(i,j,0));
            }
        }
    }
    for(int i=1;i<=4;i++){
        for(int j=1;j<=n;j++){
            if(b[i][j]&&ans[i][j]=='*'){
                vb.push_back(dfs2(i,j,0));
            }
        }
    }
    sort(va.begin(),va.end());
    sort(vb.begin(),vb.end());
    int cnt=0;
//当齐齐的连通块个数比司机的少的时候
    if(va.size()>vb.size()){
        cout<<"-1"<<endl;
    }
当齐齐的连通块个数比司机的大多的时候
    else{
        for(int i=va.size()-1;i<vb.size();i++){
            cnt+=vb[i];
        }
        cout<<cnt<<endl;
    }
}
DJelly

其实跟求走迷宫的最短路没有太大区别,只不过是从二维变成三维,在就没什么区别。

使用bfs去解。

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n;
char sum[105][105][105];
int cnt[105][105][105];
struct node{
    int x,y,z;
};
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(){
    queue<node> q;
    q.empty();
    node temp;
    temp.x=1;
    temp.y=1;
    temp.z=1;
    q.push(temp);
    while(!q.empty()){
        node ff=q.front();
        q.pop();
        node ans;
        for(int i=0;i<6;i++){
            ans.x=ff.x+dx[i];
            ans.y=ff.y+dy[i];
            ans.z=ff.z+dz[i];
            if(ans.x<1||ans.x>n||ans.y<1||ans.y>n||ans.z<1||ans.z>n) continue;
            if(sum[ans.x][ans.y][ans.z]=='*') continue;
            if(cnt[ans.x][ans.y][ans.z]!=-1) continue;
            cnt[ans.x][ans.y][ans.z]=cnt[ff.x][ff.y][ff.z]+1;
            q.push(ans);
        }
    }
}
signed main(){
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            for(int k=1;k<=n;k++){
                cin>>sum[i][j][k];
            }
        }
    }
    memset(cnt,-1,sizeof(cnt));
    cnt[1][1][1]=1;
    bfs();
    cout<<cnt[n][n][n]<<endl;
}
F送外卖

开始的时候没有读明白题,没有想清楚为什么会出现字符串无限大的时候。实际上,就是当走到某一个点的时候,如果直接走b的话会到终点,但是如果走a的话,会进入一个循环,还能再走回该点,所以在走的时候按照字典序最小,就会一直走a,而不会选b,然后就会变成一个无限长的字符串,这时候就要输出Infinity!。没有合法字符串的情况就是,无论怎么走都无法走到终点。

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n;
int a[100005],b[100005];
bool vis[100005];
bool sum[100005];
char str[100005];
int flag;
bool dfs(int x,int y){
    if(x<1||x>n) return false;//没有办法走到终点
    if(x==n){//走到终点的时候
        str[y+1]='\0';
        return true;
    }
    if(vis[x]){//有点被便利过,进入可一个循环
        sum[x]=true;
        return false;
    }
    vis[x]=true;//先选a的时候,再选b,所以遍历的时候就是先遍历a,在遍历b
    if(dfs(x+a[x],y+1)){
        str[y]='a';
        if(sum[x]) flag=1;//就是题目中说的那种循环
        return true;
    }
    if(dfs(x+b[x],y+1)){
        str[y]='b';
        if(sum[x]) flag=1;
        return true;
    }
    return false;
}
signed main(){
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++) cin>>b[i];
    memset(vis,false,sizeof(vis));
    memset(sum,false,sizeof(sum));
    if(dfs(1,0)){
        if(flag) cout<<"Infinity!"<<endl;
        else cout<<str<<endl;
    }
    else{
        cout<<"No solution!"<<endl;
    }
}
G数独挑战

先要搞清楚数独的规则,每一行中不能出现重复的数字,每一列中也不能出现重复的数字,在每一个小九宫格中也不能出现重复的数字。所以就按照上述的规则去模拟。模拟的时候使用dfs.

#include<bits/stdc++.h>
using namespace std;
#define int long long
int sum[10][10];
int hh[10][10];
int ll[10][10];
int kk[10][10][10];
struct node{
    int x,y;
}cal[100];
int flag;
int cnt=0;
void dfs(int s){
    if(flag) return ;//代表所有点已将找好了,并且已经全部输出了
    if(s==cnt){//代编所有点已经找好了
        flag=1;
        for(int i=0;i<9;i++){
            for(int j=0;j<9;j++){
                cout<<sum[i][j]<<' ';
            }
            cout<<endl;
        }
        
    }
        for(int m=1;m<=9;m++){
            int i=cal[s].x;
            int j=cal[s].y;
            if(!hh[i][m]&&!ll[j][m]&&!kk[i/3][j/3][m]){//如果当前点的坐标满足上述三个条件就将其放入并标记
                hh[i][m]=ll[j][m]=kk[i/3][j/3][m]=1;
                sum[i][j]=m;
                dfs(s+1);
                hh[i][m]=ll[j][m]=kk[i/3][j/3][m]=0;
                sum[i][j]=0;
            }
        }
    
}
signed main(){
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    for(int i=0;i<9;i++){
        for(int j=0;j<9;j++){
            cin>>sum[i][j];
            hh[i][sum[i][j]]=1;//每行所拥有的元素
            ll[j][sum[i][j]]=1;//每列所拥有的元素
            kk[i/3][j/3][sum[i][j]]=1;//每个九宫格内所拥有的元素
            if(sum[i][j]==0){
                cal[cnt].x=i;//将每一个要填入的点的坐标存进一个vector中
                cal[cnt].y=j;
                cnt++;
            }
        }
    }
    dfs(0);
}
H幸运数字Ⅱ

使用dfs将1000000000以内每一个全是由4跟7组成的数字找出来,并按大小排序,再将左端到右端中每一个每一个值对应的离他最近的幸运数字找出来,并将它们全部加起来。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int inf=10000000002;
int r,l;
vector<int> ve;
void dfs(int x){//找出全部的幸运数字
    if(x>inf) return ;
    ve.push_back(x);
    dfs(x*10+4);
    dfs(x*10+7);
}
signed main(){
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    cin>>l>>r;
    dfs(0);
    sort(ve.begin(),ve.end());
    int j=0;
    int sum=0;
    for(int i=l;i<=r;){
        while(i>ve[j]) j++;//找到距离当前数字最近的幸运数字
        sum+=ve[j]*(min(r,ve[j])-i+1);//在一定范围内的幸运数字都将是一个数字,所以只需要将那个范围找出来就可以了
        i=ve[j]+1;
    }
    cout<<sum<<endl;
}
I[NOIP2017]奶酪

实际上,先将每一个跟下表面相交的点找出来,然后再去寻找每一个跟他们相交的的奶酪求的位置,如果相交的奶酪球能够到达上表面的话就输出直接输出,反之,接着去寻找下一个与该奶酪球相交的点。

#include<bits/stdc++.h>
using namespace std;
#define int long long
int t,n,h,r,d;
int x[1005],y[1005],z[1005];
bool vis[1005];
int flag;
signed main(){
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    cin>>t;
    while(t--){
        cin>>n>>h>>r;
        memset(vis,true,sizeof(vis));
        flag=0;
        d=2*r;
        for(int i=1;i<=n;i++){
            cin>>x[i]>>y[i]>>z[i];
        }
        queue<int> q;
        for(int i=1;i<=n;i++){
            if(z[i]-r<=0&&z[i]+r>=0){//先将每一个跟下表面相交的点找出来,并存到队列中去。
                q.push(i);
                vis[i]=false;
            }
        }
        while(!q.empty()){//然后依次遍历队列中的每一个点,去找个他们有关联的点
            int k=q.front();
            q.pop();
            int dx,dy,dz,dis;
            if(z[k]+r>=h){//当前奶酪球的可以触碰到上表面的话,那么就直接标记一下,然后退出循环,不在去遍历队列剩余的点
                flag=1;
                break;
            }
            for(int i=1;i<=n;i++){
                if(vis[i]==false) continue;
                dx=abs(x[i]-x[k]);
                if(dx>d) continue;//如果x方向上相差的距离已经超过了直径,那么两点就不可能相交,直接排除就好(不直接算两点间的距离是担心如果直接算的话,数值太大,会发生爆精度的情况。
                dy=abs(y[i]-y[k]);
                if(dy>d) continue;
                dz=abs(z[i]-z[k]);
                if(dz>d) continue;
                dis=dx*dx+dy*dy+dz*dz;//在三个方向都满足条件的情况下,计算距离的平方,不开方是怕丢失精度
                if(dis<=d*d){//如果两个球相交的话就将该球放入队列中,并将该球标记
                    q.push(i);
                    vis[i]=false;
                } 
            }
        }
        if(flag) cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
}
Kwyh的迷宫

只求能不能走出迷宫,所以直接dfs硬跑一遍就好了。

#include<bits/stdc++.h>
using namespace std;
#define int long long
char a[505][505];
int t;
int n,m;
int x,y;
bool b[505][505];
int flag=0;
int dx[]={0,-1,1,0};
int dy[]={-1,0,0,1};
void dfs(int x,int y){
    if(a[x][y]=='t'){
        flag=1;
        return ;
    }
    for(int i=0;i<4;i++){
        int xx=x+dx[i];
        int yy=y+dy[i];
        if(xx<1||xx>n||yy<1||yy>m) continue;
        if(a[xx][yy]=='x') continue;
        if(b[xx][yy]==false) continue;
        b[xx][yy]=false;
        dfs(xx,yy);
    }
}
signed main(){
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    cin>>t;
    while(t--){
        cin>>n>>m;
        flag=0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                cin>>a[i][j];
                if(a[i][j]=='s'){
                    x=i;
                    y=j;
                }
            }
        }
        memset(b,true,sizeof(b));
        dfs(x,y);
        if(flag) cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
}

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值