【算法每日一练]-dfs 篇3 降雨求水坑 ,八皇后问题 ,四维数独

好,今天继续讲dfs

目录

题目:降雨求水坑

  思路: 

题目: 八皇后问题

  思路:

题目:四维数独

  思路:


        

先来道搜索题热热身

题目:降雨求水坑

    

思路: 

思路很简单,就是搜索,先找到有水的位置,然后开始填水,把遍历过的水都给填上,然后再找下一个有水的位置,找着填着,填完再找。这样就能找到水坑数了。

   

要注意的是:水坑有8个方向相连,这里要小心一下

#include<cstdio>  
using namespace std;
char a[101][101];
int ans,n,m;
void dfs(int x,int y){
    a[x][y]='.';    //标记为已走,这样既能防止重复遍历这个点,也能减少一个分支数
    int dx,dy;
    for(int i=-1;i<=1;i++){//每个分支都有9-1个方向
        for(int j=-1;j<=1;j++){
            dx=x+i;
            dy=y+j;
            if(dx>=0&&dx<=n&&dy>=0&&dy<=m&&a[dx][dy]=='W'){//如果没有超过边界且为'W'的话就往那个点继续深入搜索
                dfs(dx,dy);
            }
        }
    }
    return;//很多余,不写也没事
} 
int main(){
    scanf("%d%d",&n,&m);
    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]=='W'){//如果是W的话就直接开始遍历
                dfs(i,j);//我们自动填水坑
                ans++;//水潭加一处
            }
        }
    }
    printf("%d",ans);
    return 0;
}

(上面是代码+注释)

 好了,开始上正菜:

   


    

题目:八皇后问题

    

思路:

核心思想还是回溯,也就是试错嘛,遇错就返回到上个正确的状态。主要是怎么标记这个棋盘被皇后占领的状态呢?

  

首先啊:行列还是挺好标记的,r[x]表示第x行有皇后(对应1~n) ,  c[x]第x列有皇后(对应1~n)

那么斜着的线怎么办呢?别急!你先别急!我们不难发现:k为-1的线中的坐标有这样的规律:i-j相同(同直线上),k为1的线中的坐标有这样的规律:i+j一样(同直线上)。那么就简单了:

  

我们设置l1[ ]代表斜率为+1线,(共2n-1个线,同斜线各点i+j一样),l2[ ]代表斜率-1,(共2n-1个线,各点i-j一样)
但是l2[1-n->n-1]有负数,不妨直接优化一下变成l2[1-n+n->n-1+n]也就是l2[1->2n-1]

最后就能dfs了,上代码!!!

  

   

#include <bits/stdc++.h>                      
using namespace std;                       
int r[100],c[100],l1[100],l2[100];//标记数组千万不能越界
int ans,n,a[9];
void print()
{
    if(ans<=3)//输出前三组解法
    {
        for(int k=1;k<=n;k++)  cout<<a[k]<<" ";
        cout<<endl;
    }
}
void dfs(int x){//x表示此时放置皇后数,默认和行数一一对应
	if(x==n+1){//第n+1不用找,是结束条件
		ans++;print();return ;
	}
	for(int i=1;i<=n;i++){ //回溯n次(每行有n个选择,放置到某一列嘛)就是找错了就认错(恢复)
		if(!r[x]&&!c[i]&&!l1[i+x]&&!l2[x-i+n]){//对行,列,双斜线判断,都不能有放过皇后的
			r[x]=1;c[i]=1;l1[i+x]=1;l2[x-i+n]=1;//放下皇后,标记四个状态
			a[x]=i;dfs(x+1);//保存解法
			r[x]=0;c[i]=0;l1[i+x]=0;l2[x-i+n]=0;//回溯(恢复状态)
		}
	}
}
int main(){
	cin>>n;
	dfs(1);//开始放第一个皇后
	cout<<ans;
	return 0;
}

好的,是不是有感觉了(感觉我们自己又行了)

上一道数独的题吧

   


    

题目:四维数独

题目我记得是这样的:我们要填4*4方格,每个方格都能填入1~4,但是要保证同行,同列,四个2*2的方格都不能有重复的数字(基本就是这个意思)

   

思路:

核心思想还是 ~ 回溯!唉,对!然后我们去想一下这个标记数组怎么整?

既然行,列都不能有重复数字,那么就设置a[x][i],b[y][j],c[b][k]表示第x行已有i数字,第y列已经有了j数字,第b块号已有k数字。

  

那么4个2*2方块内也不能出现同样的数字怎么办呢?

给一个方法哈:(row-1)/2*2+(col-1)/2+1,这样就分成了4个块号。(你问我怎么来了?额……咱们会用就行了,哈哈哈哈)你真的不会了,那就if呗(弄他娘的4个if)

 

         

#include <bits/stdc++.h>            //四阶数独(回溯)  (关键是标记数组,如果当前数没出现过,那就尝试放呗)
using namespace std;
int ans;
int arr[17],a[5][5],b[5][5],c[5][5];//标记数组要够大,[4][4]是不够的
void dfs(int x){//当然可以写成二维的
	if(x==17){
		ans++;return ;
	}
	int row=(x-1)/4+1;//行号
	int col=(x-1)%4+1;//列号(不要写c)
	int block=(row-1)/2*2+(col-1)/2+1;//块号
	for(int i=1;i<=4;i++){//每个位置都有4个数可以尝试
		if(a[row][i]==0&&b[col][i]==0&&c[block][i]==0){//每行列块对应的i都未出现
			arr[x]=i;a[row][i]=1;b[col][i]=1;c[block][i]=1;//保存,标记一下状态
			dfs(x+1);a[row][i]=0;b[col][i]=0;c[block][i]=0;//回溯(恢复状态)
		}
	}
}
int main(){
	dfs(1);//从第一个格子开始填充
	cout<<ans;
	return 0;
}

好了,以上就是今天的干货了。还是有点技术含量的,相信你能看明白的!

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值