一点拙见
一. 滚动数组
马佬在解决蜜蜂路线问题时,用滚动数组对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;
}
滚动数组的作用在于优化空间。
运用在菲波那契数列中:
斐波那契数列:
一般代码:
- #include<iostream>
- #include<cstdio>
- using namespace std;
- int Fib[25];
- int fib(int n)
- {
- Fib[0] = 0;
- Fib[1] = 1;
- Fib[2] = 1;
- for(int i = 3; i <= n; ++i)
- Fib[i] = Fib[i - 1] + Fib[i - 2];
- return Fib[n];
- }
- int main()
- {
- int ncase, n, ans;
- scanf("%d", &ncase);
- while(ncase--)
- {
- scanf("%d", &n);
- ans = fib(n);
- printf("%d\n", ans);
- }
- return 0;
- }
利用滚动数组优化后代码为:
- #include<cstdio>
- using namespace std;
- int Fib[3];
- int fib(int n)
- {
- Fib[1] = 0;
- Fib[2] = 1;
- for(int i = 2; i <= n; ++i)
- {
- Fib[0] = Fib[1];
- Fib[1] = Fib[2];
- Fib[2] = Fib[0] + Fib[1];
- }
- return Fib[2];
- }
- int main()
- {
- int ncase, n, ans;
- scanf("%d", &ncase);
- while(ncase--)
- {
- scanf("%d", &n);
- ans = fib(n);
- 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节点可以延伸出2个2节点和一个3节点
每个2节点可以延伸出一个2节点和一个3节点
初始时有一个3节点(n=1)
第二层有1*(3)+2*(2)=7
第三层有3*(3)+4*(2)=17
第四层有7*(3)+10*(2)=41
得3节点的个数是上次的2,3节点个数之和,而2节点为上次3节点个数的2倍加上上次2节点的个数
设3节点个数为a,2节点个数为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;
}
所以说编程也是美的。
编程以境界为最上,有境界则自成高格,自有名句。大佬之代码所以独绝者在此。