太阳一定会从西边出来的之DFS

首先,第一题是超级台阶问题。

对于这样的问题,调用递归函数进行一遍遍的运算其实是很麻烦的,这个时候就需要进行函数初始化了,用一个空数组记录每次下标对应的结果,如果这个下标没有出现过,就对这个下标进行赋值,如果说出现过,就直接输出。

但是应该怎么输出?

应该回溯着输出,这样才能得到正确的结果。

比如说A   https://vjudge.net/contest/273364#status//A/0/  超级台阶问题

我们知道当有级台阶的时候,上楼的方案只有一种,这个时候可以把这个条件作为初始值,用a[]=1,a[]=1来表示,以后在判断的过程中,但凡发现a[]=1就输出这个值到递归的上一级。

AC代码如下:

#include<stdio.h>
#include<algorithm>
using namespace std;
int T,n;
int a[50];
int dfs(int n)
{
    if(a[n]) return a[n];
    else
        return a[n]=dfs(n-1)+dfs(n-2);
}

int main()
{
    scanf("%d",&T);
    while(T--)
    {
       a[1]=1;
       a[2]=1;
       a[3]=2;
       scanf("%d",&n);//1到40
       printf("%d\n",dfs(n));
    }
    return 0;
}

这个代码的关键点就是   a[n]=dfs(n-1)+dfs(n-2),这使得过程优化了很多很多。

接下来就是B题了。

https://vjudge.net/contest/273364#problem/B  汉诺塔问题。

解法的基本思想是递归。假设有A、B、C三个塔,A塔有N块盘,目标是把这些盘全部移到C塔。那么先把A塔顶部的N-1块盘移动到B塔,再把A塔剩下的大盘移到C,最后把B塔的N-1块盘移到C。 每次移动多于一块盘时,则再次使用上述算法来移动。

怎么在题目中说的大木块不能放在小木块的基础之上,把A塔上面的N-1个盘移动到B塔上面呢?

比如现在有三个木块,从小到大分为da zhong xiao,首先,把xiao移动到C,然后把zhong移动到b,然后把xiao移动到b,然后把da移动到c,然后把xiao移动到A,然后把zhong移动到C,然后把小移动到C

也就是说,把a上面的(n-1)个借助c移动到b,然后把a上面的第n个移动到c,然后把b上面的(n-1)个借助a移动到c

参考网站:

http://blog.csdn.net/geekwangminli/article/details/7981570 
http://www.cnblogs.com/yanlingyin/archive/2011/11/14/2247594.html(推荐这个)

那么现在知道了怎么操作后,就是怎么写代码,怎么输出了。

这个是每一步的移动过程,

具体的实现就是

if(n==1)
    {
        printf("%d:%c->%c\n",n,a,c);
        return ;
    }
  bfs(n-1,a,c,b);
  printf("%d:%c->%c\n",n,a,c);
  bfs(n-1,b,a,c);

不要管如何移动。

接下来就是C题了

https://vjudge.net/contest/273364#problem/C

每个人相邻两人身上数字之和都为素数时,便找到了一个环

那么可以在程序的开头写一个素数判断的函数,比如:

int isp[1000],flag[1000];
void is_prime()///素数打表,如果isp等于1就代表是素数
{
    int k=0;
    memset(isp,0,sizeof(isp));
    memset(flag,0,sizeof(0));
    for(int i=2; i<=7; i++)
        if(!flag[i])
            for(int j=i*i; j<=50; j+=i)
                flag[j]=1;
    for(int i=2; i<=50; i++)
        if(!flag[i])
            isp[i]=1;
}//素数打表


//这里有一个关键点就是在主函数外面,数组的初始值为0.

如果说isp【i】==1,那么,这个数就是素数

首先,记录1到index[1],然后判断2是否成立

1.如果成立的话,就把2记录到index[2],让后判断3是否成立

2.如果不成立的话,就过,判断3是否成立

想象一下,判断是否成立以后,数字不从头开始会怎么样?

如果说不从头开始,就会WRONG,如果说从开始的话,怎么从头开始呢?

这个时候就需要一个数组记录走过的数是否出现过了,(学长在讲的时候说这是初始化,表示,,,,,)

在我写的代码里面index,下面先弄出来我写的一个代码:

#include<algorithm>
#include<stdio.h>
#include<string.h>
using namespace std;
const int maxx=50;
int index[50];
int num;
int n;
int a[50];
int isp[100],flag[100];



void is_prime()///素数打表,如果isp等于1就代表是素数
{
    int k=0;
    memset(isp,0,sizeof(isp));
    memset(flag,0,sizeof(0));
    for(int i=2; i<=5; i++)
        if(!flag[i])
            for(int j=i*i; j<=24; j+=i)
                flag[j]=1;
    for(int i=2; i<=24; i++)
        if(!flag[i])
            isp[i]=1;
}//素数打表

void iniT(int n)
{
    for(int i=0;i<n;++i)
    {
        a[i]=i+1;
    }
}

void dfs(int x)
{
   if(x==n&&isp[a[0]+a[n-1]])//判断第一个数和最后一个数是否成立
   {
       for(int i=0;i<n;++i)
       printf("%d%c",a[i],i==n-1?'\n':' ');
   }
   else
    for(int i=2;i<=n;++i)
   {

       if(!index[i]&&isp[i+a[x-1]])//首先,两个数的和为素数,这个时候赋值
       {
           a[x]=i;
           index[i]=1;
           dfs(x+1);
           index[i]=0;//代表走不通
       }
   }
}

int main()
{
    is_prime();
    while(~scanf("%d",&n))
    {
        printf("Case %d:\n",++num);
        iniT(n);
        memset(index,0,sizeof(index));
        dfs(1);
        printf("\n");
    }
    return 0;
}

在写这段代码之前,我对于这道题怎么写代码还是一脸懵逼,后来想了半个多小时,然后看了看我以前写的代码开始写半个小时,发现对照以前的代码写还有好多BUG,修改了几十分钟才写出来。emmmm

从上往下,这个代码分为四部分:1.素数的函数 2.赋值的函数 3.dfs递归的调用 4.主函数。

这个是在主函数里面考虑的,每次输入都要输出Cse X:,既然X是变化的,那么就在每进行一层while循环的时候让X+1,并且后面加一个换行符

前面说index是判断一组数里面的某一个数是否用过,那么在每次的while循环开始(每次输入数据),index应该赋初值0,这样才不会出现错误。memset(index,0,sizeof(index));

并且每次都要用 iniT(n);对a进行赋值

递归的调用,从第一个数开始,dfs(1);然后往后判断。

关于这个图,意思就是从2开始,判断这个数与前面一个数的和是否是素数,你看,如果成立的话,直接走到头了,而如果不成立的话只能走一点点就走不通了,那么怎么才能把走得通与走不通表示出来呢?

首先,判断是不是已经判断了n个数,如果说判断了n个数就输出。

其次,如果说没有成立,那么该怎么办?

粗略地说就是返回上一级

 if(!index[i]&&isp[i+a[x-1]])//首先,两个数的和为素数,这个时候赋值
       {
           a[x]=i;
           index[i]=1;
           dfs(x+1);
           index[i]=0;//代表走不通
       }

也就是index[i]=0,代表这个数没有被使用

a[x]是记录可行的序列(所以用x作为下标,因为x是从1开始的)

注意:这里的index的下标是i,而不是x,为什么呢?

因为for循环,for(int i=2;i<=n;++i)。

看着个图的计算轨迹,你仔细思考思考,应该就能想出来。

这个是从小到大进行的。

dfs(x+1):就代表了一个数成立后的下一个数。

index[i]=0:代表了这个数没有被用过

比如说:上图中最上面的那个中的1 2 3 轨迹,假设2 3 是不成立的,那么index【2】就应该变成0,代表这个数没有用过。

猜你喜欢

转载自blog.csdn.net/zbq_tt5/article/details/84960079