寒假笔记·DFS

普通DFS

如题

例题:P1605 迷宫

原题地址
迷宫 【问题描述】

给定一个N*M方格的迷宫,迷宫里有T处障碍,障碍处不可通过。给定起点坐标和

终点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案。在迷宫

中移动有上下左右四种方式,每次只能移动一个方格。数据保证起点上没有障碍。

输入样例 输出样例

【数据规模】

1≤N,M≤5

输入输出格式

输入格式:
【输入】

第一行N、M和T,N为行,M为列,T为障碍总数。第二行起点坐标SX,SY,终点

坐标FX,FY。接下来T行,每行为障碍点的坐标。

输出格式:
【输出】

给定起点坐标和终点坐标,问每个方格最多经过1次,从起点坐标到终点坐标的方

案总数。

输入输出样例

输入样例#1:
2 2 1
1 1 2 2
1 2

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<string>
using namespace std;
int m,n,t,a,b,sx,sy,fx,fy,ans=0;
int qq[10][10];
void dfs(int x,int y)
{
    if(x==fx&&y==fy)
    {
        ans++;
        return;
    }
    else
    {
        if(qq[x][y]==0) return;
        else
        {
            qq[x][y]=0;//走过,如果不判定走过大概率TLE
            dfs(x,y+1);
            dfs(x,y-1);
            dfs(x+1,y);
            dfs(x-1,y);
            qq[x][y]=1;//回溯
        }
    }
    return;
}
int main()
{
    int i,j;
    memset(qq,0,sizeof(qq));
    scanf("%d%d%d",&m,&n,&t);
    scanf("%d%d%d%d",&sx,&sy,&fx,&fy);
    for(i=1;i<=m;i++)
        for(j=1;j<=n;j++)
            qq[i][j]=1;
    for(i=1;i<=t;i++)
    {
        scanf("%d%d",&a,&b);
        qq[a][b]=0;
    }
    dfs(sx,sy);
    if(qq[fx][fy]==0) ans=0;
    printf("%d\n",ans);
    return 0;
}

例题:P1219 八皇后

原题地址
题目描述

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

上面的布局可以用序列2 4 6 1 3 5来描述,第i个数字表示在第i行的相应位置有一个棋子,如下:

行号 1 2 3 4 5 6

列号 2 4 6 1 3 5

这只是跳棋放置的一个解。请编一个程序找出所有跳棋放置的解。并把它们以上面的序列方法输出。解按字典顺序排列。请输出前3个解。最后一行是解的总个数。

输入输出格式

输入格式:
一个数字N (6 <= N <= 13) 表示棋盘是N x N大小的。

输出格式:
前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。

输入输出样例

输入样例#1:
6
输出样例#1: 复制
2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int a[100],b[100],c[100],d[100];
int total;
int n;
void print()
{
    if(total<=2)//
    {
        for(int k=1;k<=n;k++)
        cout<<a[k]<<" ";
        cout<<endl;
    }
    total++;
}
void queen(int i)
{
    if(i>n)
    {
        print();
        return;
    }
    else
    {
        for(int j=1;j<=n;j++)
        {
            if((!b[j])&&(!c[i+j])&&(!d[i-j+n]))
            {
                a[i]=j;
                b[j]=1;
                c[i+j]=1;//占领对角线
                d[i-j+n]=1;//占领对角线
                queen(i+1);
                b[j]=0;
                c[i+j]=0;
                d[i-j+n]=0;
            }
        }
    }
}
int main()
{    
    cin>>n;
    memset(a,0,sizeof(a));
    memset(b,0,sizeof(b));
    memset(c,0,sizeof(c));
    memset(d,0,sizeof(d));
    queen(1);
    cout<<total<<endl;
    return 0;
}

隐藏的DFS

例题:P1218 [USACO1.5]特殊的质数肋骨 Superprime Rib

原题地址
题目描述

农民约翰的母牛总是产生最好的肋骨。你能通过农民约翰和美国农业部标记在每根肋骨上的数字认出它们。农民约翰确定他卖给买方的是真正的质数肋骨,是因为从右边开始切下肋骨,每次还剩下的肋骨上的数字都组成一个质数,举例来说: 7 3 3 1 全部肋骨上的数字 7331是质数;三根肋骨 733是质数;二根肋骨 73 是质数;当然,最后一根肋骨 7 也是质数。 7331 被叫做长度 4 的特殊质数。写一个程序对给定的肋骨的数目 N (1<=N<=8),求出所有的特殊质数。数字1不被看作一个质数。

输入输出格式

输入格式:
单独的一行包含N。

输出格式:
按顺序输出长度为 N 的特殊质数,每行一个。

输入输出样例

输入样例#1:
4
输出样例#1:
2333
2339
2393
2399
2939
3119
3137
3733
3739
3793
3797
5939
7193
7331
7333
7393
代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
int q,n;
int fun(int t)
{
    int i;
    for(i=2;i<=sqrt((double)t);i++)
    {
        if(t%i==0) return 0;
    }
    return 1;
}
void dfs(int x,int i)
{
    int j;
    if(i==n)
    {
        printf("%d\n",x);
        return;
    }
    else
    {
        for(j=1;j<=9;j++)
        {
            if(fun(x*10+j)) dfs(x*10+j,i+1);
        }
    }
    return;
}
int main()
{
    int i;
    scanf("%d",&n);
    dfs(2,1);
    dfs(3,1);
    dfs(5,1);
    dfs(7,1);
    return 0;
}

例题:P1215 [USACO1.4]母亲的牛奶 Mother’s Milk

原题地址
题目描述

农民约翰有三个容量分别是A,B,C升的桶,A,B,C分别是三个从1到20的整数, 最初,A和B桶都是空的,而C桶是装满牛奶的。有时,农民把牛奶从一个桶倒到另一个桶中,直到被灌桶装满或原桶空了。当然每一次灌注都是完全的。由于节约,牛奶不会有丢失。

写一个程序去帮助农民找出当A桶是空的时候,C桶中牛奶所剩量的所有可能性。

输入输出格式

输入格式:
单独的一行包括三个整数A,B和C。

输出格式:
只有一行,升序地列出当A桶是空的时候,C桶牛奶所剩量的所有可能性。

输入输出样例

输入样例#1:
[输入1]
8 9 10
[输入2]
2 5 10
输出样例#1:
[输出1]
1 2 8 9 10
[输出2]
5 6 7 8 9 10

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
int a,b,c;
int t2[21][21][21],t[21];//t2可判断是否曾经达到该状态。
void milk(int x,int y,int z)//搜索部分
{
    if(t2[x][y][z]) return;
    if(!x) t[z]=1;
    t2[x][y][z]=1;
y+z>b?milk(x,b,y+z-b):milk(x,y+z,0);
x+z>a?milk(a,y,x+z-a):milk(x+z,y,0);
z+y>c?milk(x,z+y-c,c):milk(x,0,z+y);
x+y>a?milk(a,x+y-a,z):milk(x+y,0,z);
y+x>b?milk(y+x-b,b,z):milk(0,y+x,z);
z+x>c?milk(z+x-c,y,c):milk(0,y,z+x);
    return;
}
int main()
{
    scanf("%d%d%d",&a,&b,&c);
    memset(t2,0,sizeof (t2));
    milk(0,0,c);
    int q=1;
    for(int i=0;i<=20;i++)
    {
        if(t[i]&&q==0) printf(" %d",i);
        if(t[i]&&q==1)
        {
            printf("%d",i);
            q=0;
        }
    }
    printf("\n");
    return 0;
}

染色问题

染色问题有时需要更加细化的处理。

例题:P1506 拯救oibh总部

原题地址
题目描述

oibh被突来的洪水淹没了>.<还好oibh总部有在某些重要的地方起一些围墙,用号表示,而一个封闭的号区域洪水是进不去的……现在给出oibh的围墙建设图,问oibh总部没被淹到的重要区域(由"0"表示)有多少。

输入输出格式

输入格式:
第一行是两个数,x和y(x,y<=500)

第二行及以下是一个由和0组成的xy的图。

输出格式:
输出没被水淹没的oibh总部的“0”的数量。

输入输出样例

输入样例#1:
样例输入1
4 5
00000
0000
0
00
00
00

样例输入2
5 5


00*
0
00*


输出样例#1:
样例输出1
1

样例输出2
5
代码:
30分代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
using namespace std;
char qq[550][550];
int dx[4]={0,0,1,-1};
int dy[4]={-1,1,0,0};
int n,m,ans=0;
void dfs(int a,int b)
{
    int i;
    if(qq[a][b]=='*'||a<0||b<0||a>n+1||b>n+1||qq[a][b]=='+') return;
    else qq[a][b]='+';
    for(i=0;i<4;i++)
        dfs(a+dx[i],b+dy[i]);
    return;
}
int main()
{
    int i,j;
    memset(qq,0,sizeof(qq));
    scanf("%d%d",&n,&m);
    getchar();
    for(i=1;i<=n;i++)
        scanf("%s",qq[i]+1);
    dfs(0,0);
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            if(qq[i][j]=='0') ans++;
    printf("%d\n",ans);
    return 0;
}

AC代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
using namespace std;
char qq[550][550];
int dx[4]={0,0,1,-1};
int dy[4]={-1,1,0,0};
int n,m,ans=0;
void dfs(int a,int b)
{
    int i,tx,ty;
    qq[a][b]='*';
    for(i=0;i<4;i++)
    {
        tx=a+dx[i];
        ty=b+dy[i];
        if(qq[tx][ty]=='0')
            dfs(tx,ty);
    }
}
int main()
{
    int i,j;
    memset(qq,0,sizeof(qq));
    scanf("%d%d",&n,&m);
    getchar();
    for(i=1;i<=n;i++)
        scanf("%s",qq[i]+1);
    for(i=1;i<=n;i++)
    {
        if(qq[i][1]=='0')
            dfs(i,1);
        if(qq[i][m]=='0')
            dfs(i,m);
    }
    for(i=1;i<=m;i++)
    {
        if(qq[1][i]=='0')
            dfs(1,i);
        if(qq[n][i]=='0')
            dfs(n,i);
    }
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            if(qq[i][j]=='0') ans++;
    printf("%d\n",ans);
    return 0;
}

反向DFS

例题:P1913 L国的战斗之伞兵

原题地址
题目描述

为了在敌国渗透作战,指挥官决定:派出伞兵前往敌国!然而敌国的风十分强烈,能让伞兵在同一高度不停转悠,直到被刮到一个无风区……(可怜的小兵)

输入输出格式

输入格式:
第一行:n、m两个正整数,表示敌国的大小。

以下n行,每行m个字符,“u”表示风向北吹;“d”表示风向南吹;“l”表示风向西吹;“r”表示风向东吹;“o”表示无风。(上北下南,左西右东)

输出格式:
一个数:表示有几个点可以放下伞兵。

输入输出样例

输入样例#1:
5 5
rrrrr
rdddr
rroll
uuuuu
uuuuu
输出样例#1:
19

数据范围:

1≤n≤1000,1≤m≤1000.
代码:
其实这题很简单,找对方法是关键。
找到无风的o点,然后开始推路径,然后将能到的点设为true,最后统计TRUE的个数就AC。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
int qq[1010][1010];
char ch[1010][1010];
int n,m;
void dfs(int a,int b)
{
    qq[a][b]=1;
    if(ch[a-1][b]=='d') dfs(a-1,b);
    if(ch[a+1][b]=='u') dfs(a+1,b);
    if(ch[a][b-1]=='r') dfs(a,b-1);
    if(ch[a][b+1]=='l') dfs(a,b+1);
}
int main()
{
    int i,j,ans=0;
    scanf("%d%d",&n,&m);
    memset(ch,0,sizeof(ch));
    getchar();
    for(i=1;i<=n;i++)
        scanf("%s",ch[i]+1);
    memset(qq,0,sizeof(qq));
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
        {
            if(ch[i][j]=='o'&&qq[i][j]==0) dfs(i,j);
        }
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            if(qq[i][j]==1) ans++;
    printf("%d\n",ans);
    return 0;
}

DFS与打表

例题:P1010 幂次方

原题地址
题目描述

任何一个正整数都可以用22的幂次方表示。例如

137=2^(7)+8+1;

137可表示为:

2(2(2)+2+2(0))+2(2+2(0))+2(0)

1315最后可表示为:

2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)
输入输出格式

输入格式:
一个正整数n(n≤20000)、。

输出格式:
符合约定的n的0,2表示(在表示中不能有空格)

输入输出样例

输入样例#1:
1315
输出样例#1:2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)
代码:
8以下的数打表。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
using namespace std;
int n;
int qq[20];
void init()
{
    int i;
    qq[0]=1;
    for(i=1;i<=19;i++)
        qq[i]=qq[i-1]*2;
}
void dabiao(int x)
{
    if(x==7) printf("2(2)+2+2(0)");
    if(x==6) printf("2(2)+2");
    if(x==5) printf("2(2)+2(0)");
    if(x==4) printf("2(2)");
    if(x==3) printf("2+2(0)");
    if(x==2) printf("2");
    if(x==1) printf("2(0)");
}
void dfs(int x)
{
    int i;
    for(i=0;qq[i]<=x;i++);
    i--;
    x-=qq[i];
    printf("2(");
    if(i>7)
    {
        dfs(i);
    }
    else dabiao(i);
    printf(")");
    if(x>0)
    {
        printf("+");
        if(x>7) dfs(x);
        else dabiao(x);
    }
    else return;
}
int main()
{
    init();
    scanf("%d",&n);
    if(n>7) dfs(n);
    else dabiao(n);
    printf("\n");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43918350/article/details/87966993