每周总结
动态规划就是通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。 动态规划常常适用于有重叠子问题和最优子结构性质的问题,简单基础dp主要是一些状态比较容易表示,转移方程比较好想,问题比较基本常见的。主要包括递推、最长递增序列,解决问题的一般步骤:
(1)建立模型,确认状态
(2)找出状态转移方程
(3)找出初始条件
最长公共子序列,例如,小美有一个由n个元素组成的序列{a1,a2,a3,…,an},她想知道其
中有多少个子序列{ap1,ap2,…,apm}(1 ≤ m ≤ n, 1 ≤ p1 < p2 ,…, < pm ≤
n),满足对于所有的i,j(1 ≤ i < j ≤ m), apipj < apjpi成立。
输入描述:
第一行一个整数n (1≤n≤100)表示序列长度。
接下来一行n个整数{a1,a2,a3,…,an}(1≤ai≤100)表示序列。
设dp[i]表示以第i个数结尾的满足条件的子序列的个数,然后全加在一起就
是答案。
#define ll long long
#define mod 1000000007
int n,a[105],flag[105][105];
ll dp[105],ans;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
flag[i][i]=1;
for(int j=i+1;j<=n;j++)
if(jlog(a[i])<ilog(a[j]))
flag[i][j]=1;}DP其实是会使用重复计算过的优化的暴力算法。化体现在,DP会有一个保留之前计算结果的数组之类的。
这种要计算一个大的问题就要之前计算好一个小的问题,那么这就是自下而上,下层是小问题,上层是大问题。例如最长公共子序列问题。// X,Y是字符串
LCS(X, Y):
m ← length[X]
n ← length[Y]
// 初始化边界,用于下面开始i=0时的c[i-1,j-1]之类的获取0值
// 初始化列为0,空过c[0,0]
for i ← 1 to m
do c[i,0] ← 0
// 初始化行为0,顺便填上c[0,0]
for j ← 0 to n
do c[0,j] ← 0
// 先行后列,是逐行的效果
// 每行
for i ← 1 to m
// 每列
do for j ← 1 to n
// 一样字符,看左上角↖,其值+1
do if x[i] = y[j]
then c[i,j] ← c[i-1,j-1] + 1
// 左上箭头是因为c[i-1,j-1]是在c[i,j]左上角
b[i,j] ← "↖"
// 不一样的字符,比较上方↑和左方←,看谁大取谁
else if c[i-1,j] ≥ c[i,j-1]
then c[i,j] ← c[i-1,j]
// 因为c[i-1,j]是在c[i,j]上一行
b[i,j] ← "↑"
else c[i,j] ← c[i,j-1]
// 因为c[i,j-1]是在c[i,j]左一列
b[i,j] ← "←"
for(int i=1;i<=n;i++)
dp[i]=1;
for(int i=1;i<=n;i++)
for(int j=i-1;j>0;j–)
if(flag[j][i])
dp[i]=(dp[i]+dp[j])%mod;
for(int i=1;i<=n;i++)
ans=(ans+dp[i])%mod;
printf("%lld\n",ans);
return 0;
}动态规划(dynamic programming)是解决最优化问题的一种途径、一种方法,而不是一种特殊算法,重点是一个建模的过程,具有很强的思维性。