动态规划
动态规划就是一种 用空间换取时间的方法
某一个问题可能要分为多个结点来求解,而一个结点总是要重复求解,就可以把这个结点的结果存储下来。这样就节省了时间,但是花费了空间。
动态规划的算法适用于后面结点的计算不影响前面结点的结果的问题。
主要就是找到 最优子结构 和重叠子问题,在这基础上找到动规方程。
数塔问题
从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少
#include<stdio.h>
int main()
{
int c;
scanf("%d",&c);
while (c--)
{
int n,i,j,ta[105][105],dp[105][105];
scanf("%d",&n);
for(i=0;i<n;i++)
{
for(j=0;j<=i;j++)
{
scanf("%d",&ta[i][j]);
}
}
dp[0][0]=ta[0][0];
dp[1][0]=ta[0][0]+ta[1][0];
dp[1][1]=ta[0][0]+ta[1][1];
for(i=2;i<n;i++)
{
dp[i][0]=dp[i-1][0]+ta[i][0];
dp[i][i]=dp[i-1][i-1]+ta[i][i];
for(j=1;j<i;j++)
{
if(dp[i-1][j-1]>dp[i-1][j])
dp[i][j]=dp[i-1][j-1]+ta[i][j];
else
dp[i][j]=dp[i-1][j]+ta[i][j];
}
}
int max=dp[n-1][0];
for(j=0;j<n;j++)
{
if(dp[n-1][j]>max)
max=dp[n-1][j];
}
printf("%d\n",max);
}
return 0;
}
把走到每一个结点时的最大的数记录下来。第i行第j个只能由第i-1行第j个或第i-1行第j-1个得到,所以只用比较这两个的大小。
刚开始不理解 写成了比较第i行第j个和第j+1个的大小
最大连续子序列 而且要求输出序列起始与结束的位置
刚开始也准备像数塔问题一样弄一个dp二维数组,这样比较方便,但是内存超出了
然后在白皮书上看到某题 设置了dp【2】【n】的数组来循环使用
然后我又用了一个max数组来记i个数的序列的和的最大值,用maxi数组来记取到这个最大值时的开始位置
然后再来找到max中的最大的 作为结果
但是这样的算法还是太复杂
借鉴了网上的一些算法以后 发现以某一个数为终点来判断比较简单
比如第i个数之前的最大子序列加上i如果比i自己要大,那就可以记录下到i的最大的子序列,这样就方便很多
#include<iostream>
using namespace std;
int main()
{
int n,i,s[10001],a[10001],f[10001],max,st,end; //f[i]为以i为结尾最长连续序列,s[i]为其开始点
bool flag;
scanf("%d",&n);
while(n)
{
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
flag=0;
for(i=1;i<=n;i++) //判断是否全为负
if(a[i]>=0)
{flag=1;break;}
if(flag) //不是全负
{
f[1]=a[1];
s[1]=1;
for(i=2;i<=n;i++)
{
if(f[i-1]+a[i]>=a[i])
{
f[i]=f[i-1]+a[i];
s[i]=s[i-1];
}
else
{
f[i]=a[i];
s[i]=i;
}
}
max=f[1];
st=1;
end=1;
for(i=2;i<=n;i++)
{
if(f[i]>max)
{
max=f[i];
st=s[i];
end=i;
}
}
cout<<max<<" "<<a[st]<<" "<<a[end]<<endl;
}
else //全负
cout<<0<<" "<<a[1]<<" "<<a[n]<<endl;
scanf("%d",&n);
}
return 0;
}
我觉得自己在写动态规划的问题时,思路总是把后一个加到前一个,而不是由前一个算后一个