dfs的一些整理

https://blog.csdn.net/u011437229/article/details/53188837  图来自这个

个人分类:

1、固定深度的深搜

类似八皇后 N皇后 hdu2553 一个小剪枝是和前面已经安放好的行比较

首先明确什么时候返回啥

接着每层的枚举范围是啥,剪枝函数、约束函数等 进入下一层

思路:每层dfs放置一行的皇后,枚举皇后放的位置,判断是否和前面的皇后有冲突。当所有行都放完了,则找到一个可行解

注意这道题不打表会T

//A
#include<iostream>
using namespace std;
int n;
int cnt;
int col[15];
void dfs(int layer)
{
    if(layer==n) ++cnt;
    else
    {
        for(int i=0;i<n;++i)//枚举每列
        {
            col[layer]=i;   //第layer个皇后被放在第i列
            int ok=true;
            for(int j=0;j<layer;++j)//枚举之前行的皇后
            {
                if(col[layer]==col[j]||layer-col[layer]==j-col[j]||layer+col[layer]==j+col[j])
                {
                    ok=false;
                    break;
                }
            }
            if(ok) dfs(layer+1);
        }
    }
}
int ans[15]={0,1,0,0,2,10,4,40,92,352,724};
int main()
{
//    for(int i=1;i<=10;++i)
//    {
//            n=i;
//            cnt=0;
//            dfs(0);
//            ans[i]=cnt;
//    }
//    for(int i=1;i<=10;++i)
//    {
//            cout<<ans[i]<<endl;
//    }
    while(cin>>n)
    {
        if(!n)
            break;

        cout<<ans[n]<<endl;

    }
}

hdu1016 Prime Ring Problem

给定n个数,从1~n形成一个环,要求相邻的两个数之和为素数

自己写的时候会忘记回溯。。。

#include<iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
const int N=30;
int prime[N];
int tag[N];
int cnt;
int vis[N],ans[N];//标记是否填充过以及每个排列的答案
int n;
void getp()
{
    memset(tag,0,sizeof(tag));
    tag[0]=tag[1]=1;
    for(int i=2;i<N;++i)
    {
        if(!tag[i])
            prime[++cnt]=i;
        for(int j=1;j<=cnt&&i*prime[j]<N;++j)
        {
            tag[i*prime[j]]=1;
            if(i%prime[j]==0) break;
        }
    }
}
//初始层数layer=1 ans[1]=1
void dfs(int layer)//从1-layer
{
    if(layer==n&&!tag[ans[n]+1])        //tag=0 表示是素数
    {
        for(int i=1;i<n;++i)
            printf("%d ",ans[i]);
        printf("%d\n",ans[n]);
    }
    else
    {
        for(int i=2;i<=n;++i)
        {
            if(!vis[i]&&!tag[ans[layer]+i]) //以当前层去搜下一层,下一层要和本层相加为素数  下一个数没有被用过
            {
                ans[layer+1]=i;
                vis[i]=1;
                dfs(layer+1);
                vis[i]=0;                   //回溯
            }
        }
    }
}
int main()
{

    getp();
    int kase=0;
    while(cin>>n)
    {
        ++kase;
        printf("Case %d:\n",kase);
        memset(ans,0,sizeof(ans));
        memset(vis,0,sizeof(vis));
        ans[1]=1;
        dfs(1);
        printf("\n");
    }
    return 0;
}

2、不固定深度的搜索

poj 1426(感觉广搜更好) 深搜限定搜索范围为19次,直接return

题意:给一个数n,找出它的倍数m(m!=0),m的数位不超过200,每一位非0即1

框架

if(found) return ;

if(满足条件) {操作;found=true;return;}

继续搜索

DFS版 要思考好下一步迭代会变成什么样

#include<iostream>
#include<queue>
using namespace std;
typedef unsigned long long LL;  //用BFS比较好,数量不会特别大 ,否则必须用unsigned long long
bool found=false;
void dfs(LL t,int n,int k) //一个很赞的方式是用变量控制是否找到解了,找到就return
{
    if(found) return ;
    if(t%n==0) {found=true; cout<<t<<endl; return ;}
    if(k==19) return ;//没有这句话的话会一直深搜导致最后越界
        dfs(t*10,n,k+1);
        dfs(t*10+1,n,k+1);

}
int main()
{
    int n;
    while(cin>>n&&n)
    {
        found=false;
        dfs(1,n,0);
    }
    return 0;
}

BFS版

#include<iostream>
#include<queue>
using namespace std;
typedef long long LL;
void bfs(int n)
{
    LL x=1;
    queue<LL>q;
    q.push(x);
    while(!q.empty())
    {
        LL y=q.front();
        q.pop();
        if(y%n==0)                  // !y%n这种写法是错的
        {
            cout<<y<<endl;
            return;
        }
        q.push(y*10);
        q.push(y*10+1);
    }

}

UVA 129

题意:按照字典序输出只用前L个字母组成的第N个hard sequence, 序列每四个字母空一格,满16组换行。序列最大长度为80

hard sequence 不存在两个相邻的相等子串

//参考了别人的代码,改了一个地方,感觉可读性更高https://blog.csdn.net/consciousman/article/details/52675360
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#define LL long long
#define INF 0x3f3f3f3f
#define N 1010
using namespace std;

int n, l, tag, cnt;
char ans[100000];
int check(char *s, int len)
{
    int x = len/2;
    for (int i = 1; i <= x; i++){    //相同的子串最长是x,这个是检查加上a[len-1]后是否存在长度为i的相邻相同子串形成 ABA 加上A就是easy的
        int flag = 1;
        for (int j = len-1, tot = 0; tot < i && flag; j--, tot++)
            if (s[j] != s[j-i]) flag = 0;    //长度为i的子串对比中只要有一个不相等就OK
        if (flag) return 0;
    }
    return 1;
}
void dfs(int len)
{
    if(!tag) return ;
    if (cnt++ == n){
        int x = 0, k = 0;
        for (int i = 0; i < len; i++){
            if (++k <= 4) putchar(ans[i]);
            if (k == 4){
                k = 0;
                if (++x == 16 && i != len-1) putchar('\n'), x = 0;
                else{
                    if (i != len-1) putchar(' ');
                }
            }
        }
        printf("\n%d\n", len);
        tag = 0;
        return;
    }
    for (int i = 0; i < l ; i++){
            ans[len] = i+'A';
            if (check(ans, len+1)) dfs(len+1);
            else ans[len] = 0;
    }

}
int main()
{
    while (scanf("%d %d", &n, &l), n || l) cnt = 0, tag = 1, dfs(0);
    return 0;
}

3、埃及分数 ——迭代加深搜

我写了三个版本的代码,第一个找到第一个答案就会退出,是错误的,

第二个是bool dfs版本的,第三个是void dfs版本的,其实都一样,不过bool 版本的容易判断结果是否找到,以前觉得bool版本很难理解,但是事实上void 版本return的地方对应看, 找到答案的return true ,继续找不可行 return false

//下面是我自己改的代码,A了,比上一个版本快了6ms
#include<iostream>
#include<cstring>
#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=1000;
LL ans[N],now[N];
LL maxd;
int ko=0;
LL find_best(LL a,LL b)//寻找1/i<a/b  i>b/a
{
    return b/a+1;
}
LL gcd(LL a,LL b)
{
    return b==0?a:gcd(b,a%b);
}
bool better(LL maxd)
{
//    for(LL d=maxd;d>=0;--d)
//    {
//       // if(now[d]<ans[d]||ans[d]==-1) return true;  大于等于都不更新  我只这么写的话没考虑大于的情况,大于也可以直接终止了
//       if(now[d]!=ans[d]) return now[d]<ans[d]||ans[d]==-1;//这样写代码很简洁 ,思路也很值得学习 感觉贪心的时候经常这么错,忘记考虑相等的情况
//    }
    return  now[maxd]<ans[maxd]||ans[maxd]==-1;
}
void dfs(LL deep,LL frac,LL aa,LL bb) //该深度下跑完所有的量
{
    bool ok;
    if(deep==maxd)
    {
        if(bb%aa) return ;  //aa!=1 该层查找失败  只到最后一层才开始确认是埃及分数吗??但是在递归的过程中每层的now[d]的修改都是经过检查的
        now[deep]=bb/aa;
//        for(int i=0;i<=deep;++i) cout<<ans[i]<<" ";
//        cout<<endl;
        if(better(deep)) memcpy(ans,now,sizeof(LL)*(deep+1));
        return ;
    }
    ok=false;
    LL i=max(find_best(aa,bb),frac);   //上一个传递下来的分母,要和现在要组成的数进行比较
    for(i;;++i)
    {
        if((maxd+1-deep)*bb<=i*aa) break;  //ok=false; return ok;
        now[deep]=i;
        //不剪枝的话就进行迭代,迭代的数为aa/bb-1/i;
        LL b2=bb*i;
        LL a2=aa*i-bb;
        LL g=gcd(a2,b2);
        dfs(deep+1,i+1,a2,b2);   //如果下一深度找到最佳答案 就return ok   深度不够就一直递归增加深度! nice! 懂啦!
    }
}
void solve(LL a,LL b)
{
    int check=0;
    for(maxd=1;;++maxd)
    {
        memset(ans,-1,sizeof(ans));
        dfs(0,find_best(a,b),a,b);
        if(ans[maxd]!=-1)
        {
            check=1;
            break;
        }
    }
    if(check)
    {
        for(int i=0;i<maxd;++i)
            printf("%lld ",ans[i]);
        printf("%lld\n",ans[maxd]);
    }
}
int main()
{
    LL a, b;
    while(cin>>a>>b)
    {
        solve(a,b);
    }
    return 0;
}

4、hdu 1010 奇偶剪枝

真的是每次做题都能wa在不同的地方,一直死,这次是dfs传递变量的时候应该用cnt+1 ,而不是cnt++,会影响向别的方向搜索

//dfs(nx,ny,cnt+1)  不能写成cnt++否则会wa
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
using namespace std;
int N,M,T;
char mp[10][10];
int vis[10][10];//这里不需要VIS数组,因为每次访问它的时候把它设置成不可访问的那种
int sx,sy,dx,dy;
int flag=0;
int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
void dfs(int x,int y,int cnt)
{

    if(x==dx&&y==dy&&cnt==T)  //如果找到了并且能退出
    {
        flag=1;
        return;
    }

    if(x<0||x>=N||y<0||y>=M) return; //我只要找一条能走的通的路径,所以这里就可以不走
    if(flag) return;
    int tmp=T-cnt-fabs(dx-x)-fabs(dy-y);
    if(tmp<0||tmp&1) return; //奇偶剪枝  T-cnt是还能走的步数,abs(dx-x)+abs(dy-y)是理论最短步数,如果奇偶性不一样一定不能走到
    for(int i=0;i<4;++i)
    {
        int nx=x+dir[i][0];
        int ny=y+dir[i][1];
        if(mp[nx][ny]!='X')  //一开始写成=='.'才能进入,那我永远也到不了终点了
        {
            mp[nx][ny]='X';
            dfs(nx,ny,cnt+1);  //不能写成cnt++,否则wa!!!!!!!!!!!!!!!!!!!!!!

            mp[nx][ny]='.';
            if(flag) return;   //如果已经找到了没有必要回退后再往剩下的三个方向搜了
        }
    }

}


int main()
{
    while(cin>>N>>M>>T)
    {
        if(!N&&!M&&!T) break;
        memset(vis,0,sizeof(vis));
        flag=0;
        int wall = 0;
       // memset(mp,0,sizeof(mp));
        for(int i=0;i<N;++i)
            for(int j=0;j<M;++j)
        {
            cin>>mp[i][j];
            if(mp[i][j]=='S')
            {
                sx=i;
                sy=j;
            }
            if(mp[i][j]=='D')
            {
                dx=i;
                dy=j;
            }
            else if(mp[i][j] == 'X') ++wall;
        }
        if(N*M - wall <= T) {cout<<"NO"<<endl; continue;}
        mp[sx][sy]='X'; //忘记置这个为不可访问了
        dfs(sx,sy,0);
        if(flag) cout<<"YES"<<endl;
        else cout<<"NO"<<endl;

    }
    return 0;
}

12/13待更新,总觉得一些需要用dfs迭代计数的题我还是有点迷,比如最大流,还有一个最小生成树计数

int dfs(int x,int a)
    {
        if(x==t||!a) return a;
        int flow=0,f=0;
        for(int &i=cur[x];i<G[x].size();++i)
        {
            Edge &e=edges[G[x][i]];
            if(d[e.to]==d[x]+1&&(f=dfs(e.to,min(a,e.cap-e.flow)))>0)  //dfs找到了一条增广路
            {
                e.flow+=f;
                edges[G[x][i]^1].flow-=f;
                flow+=f;
                a-=f;
                if(!a) break;
            }
        }
        return flow;
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值