第一周 搜索与回溯算法,贪心算法

先放上算法框架:

递归回溯法算法框架[一]

int Search(int k)

 {

 for (i=1;i<=算符种数;i++)

  if (满足条件)

     {

    保存结果

    if (到目的地) 输出解;

              else Search(k+1);

    恢复:保存结果之前的状态{回溯一步}

     }

 }

递归回溯法算法框架[二]

int Search(int k)

 {

   if  (到目的地) 输出解;

   else

    for (i=1;i<=算符种数;i++)

     if  (满足条件)

       {

        保存结果;

                     Search(k+1);

        恢复:保存结果之前的状态{回溯一步}

       }

 }

例题:

【例1】素数环:从1到20这20个数摆成一个环,要求相邻的两个数的和是一个素数。

【算法分析】

非常明显,这是一道回溯的题目。从1开始,每个空位有20种可能(即所谓的算符种数),只要填进去的数合法:与前面的数不相同;与左边相邻的数的和是一个素数(即满足的条件)。第20个数(即目的地)还要判断和第1个数的和是否素数(可以放在输出里做)

【例3】任何一个大于1的自然数n,总可以拆分成若干个小于n的自然数之和。当n=7共14种拆分方法

#include<cstdio>

#include<iostream>

#include<cstdlib>

using namespace std;

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

int search(int,int);

int print(int);

int main()

{

    cin>>n;

    search(n,1);                            

 //将要拆分的数n传递给s

    cout<<"total="<<total<<endl;              //输出拆分的方案数

    system("pause");

}

int search(int s,int t)

{

    int i;

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

     if (i<n)

//当前数i要大于等于前1位数,且不过n

      {

       a[t]=i;

//保存当前拆分的数i

       s-=i;           

//s减去数i, s的值将继续拆分

       if (s==0) print(t);                   

//当s=0时,拆分结束输出结果

         else search(s,t+1);                 

//当s>0时,继续递归

       s+=i;                                 

//回溯:加上拆分的数,以便产分所有可能的拆分

      }

}

int print(int t)

{

    cout<<n<<"=";

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

//输出一种拆分方案

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

    cout<<a[t]<<endl;

    total++;                                 

//方案数累加1

}

【例4】八皇后问题:要在国际象棋棋盘中放八个皇后,使任意两个皇后都不能互相吃。(提示:皇后能吃同一行、同一列、同一对角线的任意棋子。)

放置第i个(行)皇后的算法为:

int search(i);

 {

    int j;

   for (第i个皇后的位置j=1;j<=8;j++ )      //在本行的8列中去试

   if (本行本列允许放置皇后)

    {

     放置第i个皇后;

          对放置皇后的位置进行标记;

     if (i==8) 输出                                //已经放完个皇后

        else search(i+1);                //放置第i+1个皇后

     对放置皇后的位置释放标记,尝试下一个位置是否可行;

    }

 }

【算法分析】

        显然问题的关键在于如何判定某个皇后所在的行、列、斜线上是否有别的皇后;可以从矩阵的特点上找到规律,如果在同一行,则行号相同;如果在同一列上,则列号相同;如果同在/ 斜线上的行列值之和相同;如果同在\ 斜线上的行列值之差相同;从下图可验证: 

考虑每行有且仅有一个皇后,设一维数组A[1..8]表示皇后的放置:第i行皇后放在第j列,用A[i]=j来表示,即下标是行数,内容是列数。例如:A[3]=5就表示第3个皇后在第3行第5列上。

经过对例题和上机练习的研究,我发现这就是传说中的:暴搜。。。

值得一提的是排列除了一个一个放的另一种方法:

先读入整个序列,对于这个序列,要做的就是确定第一个放什么,轮流拿这个序列中的每个元素放进来,然后再对除了第一个以外的序列执行同样操作。

如果有重复元素,在放每一个元素之前,要判断与这个元素相同的元素之前有没有用过。放代码(允许重复元素):

 #include <iostream>
 #include <algorithm>
 #include <cstdio>
 using namespace std ;
 long long ans;
 int ok(char str[],int a ,int b )
 {
          if(b>a)
           for(int i=a;i<b;i++)
              if(str[i]==str[b])
                  return 0;
           return 1;
 }
 void perm(char str[],int k,int m)
 {
           int i;
           if(k==m)
           {
              ans++;
              for(i=0;i<=m;i++)
                  printf("%c",str[i]);
              printf(" ");
              return ;
           }
           else for(i=k;i<=m;i++)
                   if(ok(str,k,i))
                   {
                           swap(str[k],str[i]);
                           perm(str,k+1,m);
                           swap(str[k],str[i]);
                   }
   
 }
 int main()
 {
      char str[505];
      int n,i;
      scanf("%d",&n);
      getchar();
      ans=0;
      for(i=0;i<n;i++)
          scanf("%c",&str[i]);
      perm(str,0,n-1) ;
      printf("%lld ",ans);
      return 0;
 }
贪心算法
若在求解一个问题时,能根据每次所得到的局部最优解,推导出全局最优或最优目标。那么,我们可以根据这个策略,每次得到局部最优解答,逐步而推导出问题,这种策略称为贪心法。
原文地址:https://www.cnblogs.com/zkx06111/p/3659693.html