深搜与广搜

深搜

不撞南墙不回头 \color{red}{不撞南墙不回头} 不撞南墙不回头
数据<100, 否则 bfs

DFS树

在这里插入图片描述

跳马求方案数(1644)

学会设置偏移量
判越界,判重,回溯

中国象棋半张棋盘如图 1 1 1 所示。马自左下角 ( 0 , 0 ) (0,0) (0,0) 向右上角 ( m , n ) (m,n) (m,n) 跳。规定只能往右跳,不准往左跳。比如图 1 1 1 中所示为一种跳行路线,并将路径总数打印出来。

输入格式
只有一行:两个数 n n n m m m
输出格式
只有一个数:总方案数 t o t a l total total
样例输入

4 8

样例输出

37

该题无需判重和回溯 \color{red}{该题无需判重和回溯} 该题无需判重和回溯
在这里插入图片描述

迷宫求方案数(1605)

给定一个 N × M N \times M N×M 方格的迷宫,迷宫里有 T T T 处障碍,障碍处不可通过。
在迷宫中移动有上下左右四种方式,每次只能移动一个方格。数据保证起点上没有障碍。
给定起点坐标
数据范围
对于 100 % 100\% 100% 的数据, 1 ≤ N , M ≤ 5 1 \le N,M \le 5 1N,M5 1 ≤ T ≤ 10 1 \le T \le 10 1T10 1 ≤ S X , F X ≤ n 1 \le SX,FX \le n 1SX,FXn 1 ≤ S Y , F Y ≤ m 1 \le SY,FY \le m 1SY,FYm
输入格式
第一行为三个正整数 N , M , T N,M,T N,M,T,分别表示迷宫的长宽和障碍总数。
第二行为四个正整数 S X , S Y , F X , F Y SX,SY,FX,FY SX,SY,FX,FY S X , S Y SX,SY SX,SY 代表起点坐标, F X , F Y FX,FY FX,FY 代表终点坐标。
接下来 T T T 行,每行两个正整数,表示障碍点的坐标。
输出格式
输出从起点坐标到终点坐标的方案总数。和终点坐标,每个方格最多经过一次,问有多少种从起点坐标到终点坐标的方案。
输入

2 2 1
1 1 2 2
1 2

输出

1

在这里插入图片描述

棋盘问题

int n,m,res;
char g[N][N];
bool st[N];

void dfs(int x,int cnt){
    if(cnt==m){
        res++;
        return;
    }
    if(x>=n) return;
    
    for(int i=0;i<n;i++){
        if(g[x][i]=='#' && !st[i]){
            st[i]=1;
            dfs(x+1,cnt+1);
            st[i]=0;
        }
    }
    dfs(x+1,cnt);
}

int main(){
    while(cin>>n>>m, n>0 || m>0){
        char g[N][N];
        for(int i=0;i<n;i++) cin>>g[i];
        // 为什么不能从1开始?
        //虽然行是从1开售
        //但列还是从0开始
       
        res=0;
        dfs(0,0);
        cout<<res<<endl;
    }

    return 0;
}

八皇后(1219)

一定要注意挖掘对角线与坐标隐含的映射关系
判越界,判重,回溯
0开始if(x==n);从1开始if(x>n)

一个如下的 6 × 6 6 \times 6 6×6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。

上面的布局可以用序列 2   4   6   1   3   5 2\ 4\ 6\ 1\ 3\ 5 2 4 6 1 3 5 来描述,第 i i i 个数字表示在第 i i i 行的相应位置有一个棋子,如下:
行号 1   2   3   4   5   6 1\ 2\ 3\ 4\ 5\ 6 1 2 3 4 5 6
列号 2   4   6   1   3   5 2\ 4\ 6\ 1\ 3\ 5 2 4 6 1 3 5
这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 3 3 3 个解。最后一行是解的总个数。
输入格式
一行一个正整数 n n n,表示棋盘是 n × n n \times n n×n 大小的。
【数据范围】
对于 100 % 100\% 100% 的数据, 6 ≤ n ≤ 13 6 \le n \le 13 6n13
输出格式
前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。输入

6

输出

2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4
int n,q[N],p[N],pos[N],c[N],ans;

void print(){
    if(ans<=3){
        rep(i,1,n){
            printf("%d ",pos[i]);
        }
        puts("");
    }
}

void dfs(int i){
    if(i>n){
        ans++;
        print();
        return;
    }

    rep(j,1,n){
        if(c[j] || q[i+j] || p[i-j+n]) continue;
        pos[i]=j;//记录i行放在第j列
        c[j]=q[i+j]=p[i-j+n]=1;
        dfs(i+1);
        c[j]=q[i+j]=p[i-j+n]=0;
    }
}

int main(){
    cin>>n;

    dfs(1);//是1不是n
    printf("%d",ans);

    return 0;
}

选数

选数+判断素数(质数)

int n,m,res;
int a[N],q[N];

int prime(int x){
    if(x==1) return 0;
    rep(i,2,x/i)
        if(x%i==0) return 0;
    return 1;
}

void dfs(int x,int y){
    if(x+n-y<m) return;
    //!!!
    if(x>m){
        ll sum=0;
        rep(i,1,m) sum+=a[i];
        if(prime(sum)) res++;
        return;
    }
    rep(i,y,n){
        a[x]=q[i];//
        dfs(x+1,i+1);
        a[x]=0;
    }
}

int main(){
    cin>>n>>m;
    rep(i,1,n) cin>>q[i];
    dfs(1,1);
    cout<<res;

    return 0;
}

P2089 烤鸡 指数

int n,res;
int a[N],g[N][10+5];

//x:十种配料的location
//y:当前已选调料的总质量
void dfs(int x,int y){
    if(y>n) return;
    if(x>10){
        if(y==n){
        res++;
        rep(i,1,10) g[res][i]=a[i];
        }
        return;
    }
    rep(i,1,3){
        a[x]=i;
        dfs(x+1,y+i);//!!!
    }
}

int main(){
    cin>>n;
    dfs(1,0);
    cout<<res<<endl;
    rep(i,1,res){
        rep(j,1,10)
            cout<<g[i][j]<<' ';
        puts("");
    }
        
            

    return 0;
}

P1088 火星人 全排列

int n,m;
int a[N];

int main(){
    cin>>n>>m;
    rep(i,1,n) cin>>a[i];
    //next_permutation(a+1,a+1+n):执行数组的范围
    //rep(i,1,m):
    rep(i,1,m) next_permutation(a+1,a+1+n);
    rep(i,1,n) cout<<a[i]<<' ';
    return 0;
}

P1149 火柴棒等式 指数 + 预处理

int n,res;
int a[N];
//num[N]:其余为0,num[]:真就只有10个
int num[100010]={6,2,5,5,4,5,6,3,7,6};

// int cal(int x){
//     if(num[x]) return num[x];
//     else{
//         int cnt=0;
//         while(x){
//             cnt+=num[x%10];
//             x/=10;
//         }
//         return cnt;
//     }
// }
//TLE就剪枝
void dfs(int x,int sum){
    if(sum>n) return;

    if(x>3){
        if(a[1]+a[2]==a[3] && sum==n) res++;
        // rep(i,1,3) cout<<a[i]<<' ';
        // puts("");
        return;
    }
    rep(i,0,1000){
        a[x]=i;
        dfs(x+1,sum+num[i]);
        a[x]=0;
    }
}

int main(){
    cin>>n;
    n-=4;
    //记忆化搜索
    rep(i,10,100010) num[i]=num[i%10]+num[i/10];
    dfs(1,0);

    cout<<res;

    return 0;
}

P2036 PERKET 指数

int n,has;
int a[N],b[N],st[N];
int res=1e9;

void dfs(int x){
    if(x>n){
        has=0;//判断调料
        int sum1=1,sum2=0;
        rep(i,1,n){
            if(st[i]==1){
                has=1;
                sum1*=a[i];
                sum2+=b[i];
            }
        }
        if(has) res=min(res,abs(sum1-sum2));
        return;
    }
    st[x]=1;
    dfs(x+1);
    st[x]=0;

    st[x]=2;
    dfs(x+1);
    st[x]=1;
}

int main(){
    cin>>n;
    rep(i,1,n) cin>>a[i]>>b[i];
    dfs(1);
    cout<<res;


    return 0;
}

P1683 入门

int n,m,res;
char g[N][N];
bool st[N][N];
int dx[]={-1,0,1,0},dy[]={0,-1,0,1};

void dfs(int x,int y){
    for(int i=0;i<4;i++){
        int a=x+dx[i],b=y+dy[i];
        if(x<0 || x>=n || y<0 || y>=m) continue;
        if(g[a][b]!='.') continue;
        if(st[a][b]) continue;
        st[a][b]=1;
        res++;
        dfs(a,b);
    }

}

int main(){
    cin>>m>>n;

    // trick
    for(int i=0;i<n;i++) cin>>g[i];

    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
            if(g[i][j]=='@'){
                st[i][j]=true;
                dfs(i,j);
            }
    res++;
    cout<<res;


    return 0;
}

P1596 Lake Counting S


int n,m,res;
char g[N][N];
bool st[N][N];
int dx[]={1,1,1,0,0,-1,-1,-1};
int dy[]={-1,0,1,-1,1,1,0,-1};

void dfs(int x,int y){
    for(int i=0;i<8;i++){
        int a=x+dx[i],b=y+dy[i];
        if(x<0 || x>=n || y<0 || y>=m) continue;
        if(g[a][b]!='W') continue;
        if(st[a][b]) continue;

        st[a][b]=1;
        dfs(a,b);
    }
}

int main(){
    cin>>n>>m;
    for(int i=0;i<n;i++) cin>>g[i];

    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
            if(g[i][j]=='W' && !st[i][j]){
                res++;
                dfs(i,j);
            }
    cout<<res;

    return 0;
}

acw1114.棋盘问题 排列

int n,m;
char g[N][N];
bool st[N];
int res;//方案数

void dfs(int x,int cnt){
    if(cnt==m){
        res++;
        return;
    }
    if(x>=n) return;

    //枚举列
    for(int i=0;i<n;i++){
        if(!st[i] && g[x][i]=='#'){
            st[i]=1;
            dfs(x+1,cnt+1);
            st[i]=0;
        }
    }
    dfs(x+1,cnt);
}

int main(){
    while(cin>>n>>m,n>0 && m>0){
        for(int i=0;i<n;i++) cin>>g[i];
        res=0;
        dfs(0,0);
        cout<<res<<endl;

    }

    return 0;
}

P1025 数的划分 组合

int n,m;
int res;

void dfs(int x,int s,int sum){
    if(x>m){
        if(sum==n) res++;
        return;
    }

    rep(i,s,n-sum){//剪枝
        dfs(x+1,i,sum+i);
    }
}

int main(){
    cin>>n>>m;

    dfs(1,1,0);
    cout<<res;

    return 0;
}

P1019 单词接龙 指数 + 预处理

int n,res;
string w[N];//
int used[N];//每个单词的使用次数
int g[N][N];//存第i个单词能否连接到第j个单词,就是重合的长度

void dfs(string dragon,int x){
    res=max(res,(int)dragon.size());

    used[x]++;
    for(int i=0;i<n;i++)//这里还会出现一次
        if(g[x][i] && used[i]<2)
            dfs(dragon+w[i].substr(g[x][i]),i);
    used[x]--;
}

int main(){
    cin>>n;

    for(int i=0;i<n;i++) cin>>w[i];

    char s;
    cin>>s;

    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++){
            string a=w[i],b=w[j];
            for(int k=1;k<min(a.size(),b.size());k++){
                if(a.substr(a.size()-k,k)==b.substr(0,k)){
                    g[i][j]=k;
                    break;
                }
            }
        }
    for(int i=0;i<n;i++) 
        if(w[i][0]==s)
            dfs(w[i],i);
    cout<<res;

    return 0;
}

宽搜

一石惊起千层浪 \color{red}{一石惊起千层浪} 一石惊起千层浪
如果强行在dfs存在最优解的情况使用 bfs, 容易MLE
BFS用到队列所具有的性质:单调性和二段性

在这里插入图片描述

经典BFS-马的遍历

数组模拟队列 \color{RED}{数组模拟队列} 数组模拟队列

若队不空,则队头永远在队尾的左边

int n,m,x,y;
int d[N][N];
// 坐标对齐必须严谨
int dx[]={2,2,1,1,-1,-1,-2,-2},dy[]={1,-1,2,-2,2,-2,1,-1};
PII q[N*N];// 矩阵面积大小

void bfs(){
    ms(d,-1);
    d[x][y]=0;
    q[0]={x,y};

    int hh=0,tt=0;
    while(hh<=tt){
        auto t = q[hh++];
        for(int i=0;i<8;i++){
            int a=t.fi+dx[i],b=t.se+dy[i];
            if(a<1 || a>n || b<1 || b>m) continue;
            if(d[a][b]>=0) continue;

            d[a][b]=d[t.fi][t.se]+1;
            q[++tt]={a,b};

        }
    }
}

int main(){
    cin>>n>>m>>x>>y;
    bfs();

    rep(i,1,n){
        rep(j,1,m)
            printf("%-5d",d[i][j]);
            puts("");
    }
    
        
    return 0;
}

好奇怪的游戏

每个起点有每个起点的目标,类似于单人模式
起点入队为q[0]={x,y}

int dx[]={1,2,2,1,-1,-2,-2,-1,2,2,-2,-2},dy[]={2,1,-1,-2,2,1,-1,-2,2,-2,2,-2};
int d[N][N];
PII q[N*N];
int x,y;

int bfs(){
    ms(d,-1);
    ms(q,0);
    q[0]={x,y};
    d[x][y]=0;
    // st[x][y]=1;
    
    int hh=0,tt=0;
    while(hh<=tt){
        auto t=q[hh++];
        // if(x==1 && y==1) return d[x][y];
        for(int i=0;i<12;i++){
            int a=t.fi+dx[i],b=t.sc+dy[i];
            if(a<=0 || a>100 || b<=0 || b>100) continue;
            if(d[a][b]>=0) continue;
            // if(st[a][b]) continue;
            
            d[a][b]=d[t.fi][t.sc]+1;
            if(a==1 && b==1) return d[a][b];
            // cout<<d[a][b]<<endl;
            // st[a][b]=1;
            q[++tt]={a,b};
        }
    }
    return -1;

}

int main(){
    rep(i,1,2){
        cin>>x>>y;
        cout<<bfs()<<endl;
    }
    return 0;
}

多源BFS

多个起点同时搜,最先到达的点的距离便是最短路
多个起点只要找到目标就返回,类似于团队游戏
起点入队为q[++tt]={x,y}

//多源BFS
int n,m,a,b;
int d[N][N];
PII q[N*N];
int hh=0,tt=-1;
int dx[]={-1,0,1,0},dy[]={0,-1,0,1};

void bfs(){
    while(hh<=tt){
        auto t=q[hh++];
        for(int i=0;i<4;i++){
            int a=t.fi+dx[i],b=t.se+dy[i];
            if(a<1 || a>n || b<1 || b>m) continue;
            if(d[a][b]>=0) continue;
            d[a][b]=d[t.fi][t.se]+1;
            q[++tt]={a,b};
        }
    }
}

int main(){
    ms(d,-1);
    cin>>n>>m>>a>>b;
    while(a--){
        int x,y;
        cin>>x>>y;
        q[++tt]={x,y};
        d[x][y]=0;
    }
    bfs();
    while(b--){
        int x,y;
        cin>>x>>y;
        cout<<d[x][y]<<endl;
    }
    return 0;
}

双向BFS

如果知道起点和终点的状态,便可以降低时间和空间

双端队列(deque)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值