一点拙见

一点拙见


一.     滚动数组

马佬在解决蜜蜂路线问题时,用滚动数组对dp进行优化。原题及代码如下:

4、蜜蜂路线

【问题描述】

        一只蜜蜂在下图所示的数字蜂房上爬动,已知它只能从标号小的蜂房爬到标号大的相邻蜂房,现在问你:蜜蜂从蜂房M开始爬到蜂房N,M<N,有多少种爬行路线?

 

 

 

 

 

 


【输入格式】

 输入M,N的值。

【输出格式】

 爬行有多少种路线。

【输入样例】bee.in

   1  14

【输出样例】bee.out

   377

#include<iostream>

#include<string>

#include<cstring>

#include<cstdio>

using namespace std;

int a[1001]={0,1},b[1001]={0,2},c[1001];

int main()

{

        //freopen("bee.in","r",stdin);

   //freopen("bee.out","w",stdout);

      intm,n,l=1,x=0; 

   cin>>m>>n; 

    if(n-m==1)

    {

        printf("1");

        return 0;

        }

        if(n-m==2)

        {

                 printf("2");

                 return0;

        }

    for(inti=3;i<=n-m;i++) 

    {                                                                                                                                                

        for(intj=1;j<=l;j++)

        {

           c[j]=a[j]+b[j]+x;

            x=0;

           if(c[j]>9)

            {

               c[j]-=10;

               x=1;

               if(j==l)

                   l++;

            }

        }

       memcpy(a,b,sizeof(b)); 

       memcpy(b,c,sizeof(c)); 

    } 

   while(c[l]==0)l--;

    for(inti=l;i>=1;i--)

       cout<<c[i];

   //fclose(stdin);

   //fclose(stdout);

    return 0;

滚动数组的作用在于优化空间。

运用在菲波那契数列中:

斐波那契数列:

一般代码:

  1. #include<iostream>  
  2. #include<cstdio>  
  3. using namespace std;  
  4. int Fib[25];  
  5.   
  6. int fib(int n)  
  7. {  
  8.     Fib[0] = 0;  
  9.     Fib[1] = 1;  
  10.     Fib[2] = 1;  
  11.     for(int i = 3; i <= n; ++i)  
  12.         Fib[i] = Fib[i - 1] + Fib[i - 2];  
  13.     return Fib[n];  
  14. }  
  15.   
  16. int main()  
  17. {  
  18.     int ncase, n, ans;  
  19.     scanf("%d", &ncase);  
  20.     while(ncase--)  
  21.     {  
  22.         scanf("%d", &n);  
  23.         ans = fib(n);  
  24.         printf("%d\n", ans);  
  25.     }  
  26.     return 0;  
  27. }  


利用滚动数组优化后代码为:

  1.    
  2. #include<cstdio>  
  3. using namespace std;  
  4. int Fib[3];  
  5.   
  6. int fib(int n)  
  7. {  
  8.     Fib[1] = 0;   
  9.     Fib[2] = 1;  
  10.     for(int i = 2; i <= n; ++i)  
  11.     {  
  12.         Fib[0] = Fib[1];   
  13.         Fib[1] = Fib[2];  
  14.         Fib[2] = Fib[0] + Fib[1];  
  15.     }  
  16.     return Fib[2];  
  17. }  
  18.   
  19. int main()  
  20. {  
  21.     int ncase, n, ans;  
  22.     scanf("%d", &ncase);  
  23.     while(ncase--)  
  24.     {  
  25.         scanf("%d", &n);  
  26.         ans = fib(n);  
  27.         printf("%d\n", ans);  

28.    }  return 0;  

整理了一下网上的资料:

利用滚动数组的话在N很大的情况下可以达到压缩存储的作用。不过经常还是用在DP题目中,因为DP题目是一个自下而上的扩展过程,我们常常用到是连续的解,前面的解往往舍弃!所以用滚动数组可以说是很有必要的。

 

二.     踩方格问题

——dp的一点感悟

4982:踩方格

描述

有一个方格矩阵,矩阵边界在无穷远处。我们做如下假设:
a.    
每走一步时,只能从当前方格移动一格,走到某个相邻的方格上;
b.    
走过的格子立即塌陷无法再走第二次;
c.    
只能向北、东、西三个方向走;
请问:如果允许在方格矩阵上走n步,共有多少种不同的方案。2种走法只要有一步不一样,即被认为是不同的方案。

输入

允许在方格上行走的步数n(n <= 20)

输出

计算出的方案数量

代码:

#include<iostream>

usingnamespace std;

inta[21];

intmain()

{

    int n;

    cin>>n;

    a[1]=3;

    a[2]=7;

    if (n<=2)

    {

        cout<<a[n]; return 0;

    }

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

        a[i]=2*a[i-1]+a[i-2];

    cout<<a[n];

    return 0;

}next_permutation

这种找规律题目是一种从特殊到一般的归纳(尽管数学中不完全归纳常常会导致错误的答案,如仍留给后人思考的Fermat数,哥德巴赫猜想),但由于完全归纳推理具有一定的局限性和不可实现性,当需要归纳推理的单位数量过大,不完全归纳也是一种解决问题不错的的办法。

找递推关系式(状态转移方程),如dalao们所言,这个数一般与前一个数或前两个数有某种联系。所以这也是一种找规律很好的思路。

 

但不少规律可以通过推理的方法找出,以下为两位网友的发言:

让我们把可以延伸出3条路径的点称为3节点,2节点同理 
有:每个3节点可以延伸出22节点和一个3节点
每个2节点可以延伸出一个2节点和一个3节点
初始时有一个3节点(n=1
第二层有1*3+2*2=7
第三层有3*3+4*2=17
第四层有7*3+10*2=41
3节点的个数是上次的23节点个数之和,而2节点为上次3节点个数的2倍加上上次2节点的个数
3节点个数为a2节点个数为b 
因此得到递推式:
a[1]=1,b[1]=0;
f(n)=a[n]*3+b[n]*2;
a[n]=a[n-1]+b[n-1];
b[n]=2*a[n-1]+b[n-1];

l[i]表示最后一步向左走到达第i个格,那么它上一格不能是从右边走得到,
r[i]表示最后一步向右走到达第i个格,那么它上一格不能是从左边走得到,
u[i]表示最后一步先上走到达第i个格;

 

#include<iostream>

#include<cstdio>

using namespace std;

int n,ans;

int l[30],r[30],u[30];

int main()

{

    cin>>n;

    if (n==1) cout<<3;

      else

       { 

          l[1]=1;

          r[1]=1;

          u[1]=1;

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

            {

              l[i]=l[i-1]+u[i-1];

              r[i]=r[i-1]+u[i-1];

              u[i]=l[i-1]+r[i-1]+u[i-1];

            }

          ans=l[n]+r[n]+u[n];

          cout<<ans<<endl;

       }

    return 0;

}

 

 

所以说编程也是美的。

编程以境界为最上,有境界则自成高格,自有名句。大佬之代码所以独绝者在此。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值