深度优先搜索——dfs(1)

一维——穷举数

【codevs1294】全排列

Luogu-1706

题目描述 Description

给出一个n, 请输出n的所有全排列

输入描述 Input Description

读入仅一个整数n   (1<=n<=10)

输出描述 Output Description

一共n!行,每行n个用空格隔开的数,表示n的一个全排列。并且按全排列的字典序输出。

样例输入 Sample Input

3

样例输出 Sample Output

1 2 3

1 3 2

2 1 3

2 3 1

3 1 2

3 2 1

方法一:

#include<iostream>

#include<cstdio>

using namespace std;

int n,pd[21],used[21];

void print()

{

       int i;

       for(i=1;i<=n;++i)

              printf("%d ",used[i]);

       printf("\n");

}

void dfs(int k)

{

       int i;

       if(k==n){print();return;}

       for(i=1;i<=n;i++)

              if(!pd[i]){

                     pd[i]=1;used[k+1]=i;

                     dfs(k+1);

                     pd[i]=0;

              }

}

int main()

{

       cin>>n;

       dfs(0);

       return 0;

}

方法二:

#include<cstdio>

#include<iostream>

using namespace std;

int a[10],used[10];

int n;

void print()

{

       for(int i=1;i<=n;i++)

              printf("%d ",a[i]);

       cout<<endl;

}

void dfs(int k)

{

       for(int i=1;i<=n;i++)

              if(used[i]==0)

              {used[i]=1;a[k]=i;dfs(k+1);used[i]=0;}

       if(k==n)print();

}

int main()

{

       cin>>n;

       dfs(1);

       return 0;

}

【codevs4069】2194 N皇后

题目描述 Description

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

列号

 

1

2

3

4

5

6

1

 

O

 

 

 

 

2

 

 

 

O

 

 

3

 

 

 

 

 

O

4

O

 

 

 

 

 

5

 

 

O

 

 

 

6

 

 

 

 

O

 

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

行号 1 2 3 4 5 6

列号 2 4 6 1 3 5

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

输入描述 Input Description

一个数字N表示棋盘是N x N大小的。

输出描述 Output Description

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

样例输入 Sample Input

6

样例输出 Sample Output

2 4 6 1 3 5

3 6 2 5 1 4

4 1 5 2 6 3

4

数据范围及提示 Data Size & Hint

6 <= N <= 13

 

#include<iostream>

using namespace std;

int n,sum,a[15];

bool b[100]={0},c[100]={0},d[100]={0};

void print()

{

       sum++;//统计总方案数

       if(sum<=3){//前三个方案打印

              for(int i=1;i<=n;i++){

                     cout<<a[i];

                     if(i!=n)cout<<' ' ;

                     }

              cout<<endl;

              }

}

void queen(int i) // 做第i行

{

       for(int j=1;j<=n;j++){ //穷举i行的j列

              if((b[j]==0)&&(c[i+j]==0)&&(d[i-j+n]==0)){ //可放

                     a[i]=j;//第i行放置在j列

                     b[j]=1; //标记j列不再可放

                     c[i+j]=1; //标记副对角线及平等线不再可放

                     d[i-j+n]=1; //标记主对角线及平等线不再可放

                     if(i==n)print();//第n行都放好了,就打印

                            else queen(i+1); //搜索下一行

                     b[j]=0;//回溯时,清放置标志

                     c[i+j]=0;

                     d[i-j+n]=0;

                     }

       }

}

int main()

{

       cin>>n;

       queen(1);//从第一行开始做

       cout<<sum;

       return 0;

}

【codevs1039】NOIP2001数的划分

题目描述

将整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序)。  例如:n=7,k=3,下面三种分法被认为是相同的。1,1,5; 1,5,1; 5,1,1;问有多少种不同的分法。

输入

输入:n,k (6<n<=200,2<=k<=6)

输出

输出:一个整数,即不同的分法。

样例输入

7 3

样例输出

4

提示

输入: 7 3

输出:4 {四种分法为:1,1,5;1,2,4;1,3,3;2,2,3;}

#include<iostream>

#include<cstdio>

using namespace std;

int n,k,ans=0,a[21];

void dfs(int t,int s,int b)

{

       int i;

       if(s==n&&t==k){ans++;return;}

       if(s==n||t==k)return;

       for(i=b;i<=n-s;i++)

              if(s+i<=n)dfs(t+1,s+i,i);

}

int main()

{

       int i;

       cin>>n>>k;

       dfs(0,0,1);

       cout<<ans;

       return 0;

}

 

整数拆分——组合的和

来源:http://218.5.5.242:9018/JudgeOnline/problem.php?id=1261

题目描述

一个正整数N(N<=20)可以划分成若干个正整数的和的形式,例如5可以划分成以下几种形式:

5=1+1+1+1+1

5=1+1+1+2

5=1+1+3

5=1+2+2

5=1+4

5=2+3

5=5

编写一个程序,输入任意一个不大于20的整数N,按以上格式输出它的所有拆分。

输入

只有一行,包含一个整数N(N不超过20)。

输出

按样例中格式,输出对N的所有拆分。

样例输入

5

样例输出

5=1+1+1+1+1

5=1+1+1+2

5=1+1+3

5=1+2+2

5=1+4

5=2+3

5=5

提示

#include<iostream>

using namespace std;

int a[10001]={1},n,total;

int print(int t)

{

       cout<<n<<"=";

       for(int i=1;i<=t-1;i++)

              cout<<a[i]<<"+";

       cout<<a[t]<<endl;

}

int dfs(int s,int t)//s为剩余数,t为所填写的格子

{

       int i;

       for(i=a[t-1];i<=s;i++)//穷举可放数,当前格的数>=前一格,加法交换律

              if(i<n){

                     a[t]=i;//第t格放i这个数

                     s-=i;

                     if(s==0)print(t);//剩余数为0,则打印

                            else dfs(s,t+1);

                     s+=i;

              }

}

int main()

{

       cin>>n;

       dfs(n,1);//剩余数为n,从第一格开始做

       cout<<n<<'='<<n;

       return 0;

}

【codevs3895】环素数

题目描述 Description

给定一个N,求1——N组成的环,使得环上相邻的元素和为素数。

输入描述 Input Description

一个整数N

输出描述 Output Description

把1放在第一位置,按照字典顺序不重复地输出所有解(顺时针,逆时针算不同的两种),相邻两数之间严格用一个空格隔开,每一行的末尾不能有多余的空格。如果无解,则输出“no”。

样例输入 Sample Input

8

样例输出 Sample Output

1 2 3 8 5 6 7 4

1 2 5 8 3 4 7 6

1 4 7 6 5 8 3 2

1 6 7 4 3 8 5 2

数据范围及提示 Data Size & Hint

1<=N<=10

没过程序

#include<iostream>

#include<cstdio>

using namespace std;

int n,ans[21];

bool used[21];

void print()

{

       for(int i=1;i<=n;i++)

              printf("%d ",ans[i]);

       printf("\n");

}

bool is_prime(int x)

{

       if(x==3||x==5||x==7||x==11||x==13||x==17||x==19)return 1;

       return 0;

}

void search(int k)

{

       if(k==n+1&&is_prime(ans[1]+ans[n])){print();return;}

       for(int i=2;i<=n;i++){

              if(used[i])continue;

              if(k==1||is_prime(i+ans[k-1])){

                     ans[k]=i;used[i]=1;

                     search(k+1);

                     used[i]=0;

                     }

              }

}

int main()

{

       cin>>n;

       ans[1]=1;used[1]=1;

       if(n%2==0)search(2);

              else cout<<"no";

       return 0;

}

Luogu-P1595 信封问题

题目描述

某人写了n封信和n个信封,如果所有的信都装错了信封。求所有信都装错信封共有多少种不同情况。

输入输出格式

输入格式:

一个信封数n

输出格式:

一个整数,代表有多少种情况。

输入输出样例

输入样例#1

样例1:

2

样例2:

3

输出样例#1

样例1

1

样例2

2

方法一:dfs

#include<iostream>

using namespace std;

int n,s,a[10001];

void dfs(int x)

{

       if(x==n){

              if(a[x]!=0)s++;//如果搜到最后一个都满足条件,计数器加一

              return ;

              }

       for(int i=1;i<=n;i++)//枚举可能情况

              if(a[i]==0&&i!=x){

                     a[i]++;

                     dfs(x+1);

                     a[i]--;

                     }

}

int main()

{

       cin>>n;

       dfs(1);

       cout<<s;

       return 0;

}

方法二递推

#include<iostream>

#include<cstdio>

using namespace std;

int main()

{

       int n,m,f[100],sum=0;

       cin>>n;

       f[1]=0;f[2]=1;//初始化

       for (int i=3;i<=n;i++){

              f[i]=(i-1)*(f[i-1]+f[i-2]);// 递推

              }

       cout<<f[n];//输出结果

       return 0; 

}

Luogu-P1028 数的计算

题目描述

我们要求找出具有下列性质数的个数(包含输入的自然数n):

先输入一个自然数n(n<=1000),然后对此自然数按照如下方法进行处理:

1.不作任何处理;

2.在它的左边加上一个自然数,但该自然数不能超过原数的一半;

3.加上数后,继续按此规则进行处理,直到不能再加自然数为止.

输入输出格式

输入格式:

一个自然数n(n<=1000)

输出格式:

一个整数,表示具有该性质数的个数。

输入输出样例

输入样例#1

6

输出样例#1

6

说明

满足条件的数为

6,16,26,126,36,136

方法一:dfs

#include<cstdio>

#include<cstring>

#include<iostream>

using namespace std;

int a[10001],n,ans=1; //总数的初始值是1,因为我在搜索时没有考虑输入的数本身

int dfs(int t)

{

       int i;

       for (i=1;i<=a[t-1]/2;i++){//下一位一定是1到前一位的一半

              a[t]=i;//记录

              ans++; //累计,这个值不用回复

              dfs(t+1);

              a[t]=0;

              }

}

int main()

{

       scanf("%d",&n);

       a[1]=n;

       dfs(2); //搜索

       printf("%d",ans);

       return 0;

}

方法二递推

#include<cstdio>

#include<cstring>

#include<iostream>

using namespace std;

int a[10001],n,ans=1;

int main()

{

       int i,j;

       scanf("%d",&n);

       a[1]=1;

       for(i=2;i<=n;++i){

              a[i]=1;

              for(j=1;j<=i/2;++j)

                     a[i]=a[i]+a[j];

              }

       printf("%d",a[n]);

       return 0;

}

方法三记忆化搜索

#include<iostream>

#include<cstdio>

#include<cstdlib>

using namespace std;

long long a[100005];

int n;

long long dfs(int s)

{

       int i;

       if(a[s]!=-1) return a[s];

       long long ans=1;

       for(i=1;i<=s/2;i++)

              ans+=dfs(i);

       a[s]=ans;

       return ans;

}

int main()

{

       int i;

       scanf("%d",&n);

       for(i=1;i<=n;i++)a[i]=-1;

       a[1]=1;

       printf("%lld",dfs(n));

       return 0;

}

posted on 2017-11-20 21:48  wenjie2002  阅读(846)  评论(0编辑  收藏  举报

导航