mkfoyw/leetcode

动态规划

mkfoyw opened this issue · 5 comments

常用的一些动态规划的状态转移方式

在一个全是由 0 或者 1 矩形中, 找出全为1所构成的最大正方形面积。

dp[i][j]: 表示以第 i 行 j 例为右下角所构成最大正方形的面积

dp[i][j]: = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1

最长单调递增子序列

相关题目:

数位dp

数位:把一个数字按照个、十、百、千等等一位一位地拆开,关注它每一位上的数字。如果拆的是十进制数,那么每一位数字都是 0~9,其他进制可类比十进制。

数位 DP:用来解决一类特定问题,这种问题比较好辨认,一般具有这几个特征:

  • 要求统计满足一定条件的数的数量(即,最终目的为计数);
  • 这些条件经过转化后可以使用「数位」的**去理解和判断;
  • 输入会提供一个数字区间(有时也只提供上界)来作为统计的限制;
  • 上界很大(比如 ),暴力枚举验证会超时。

模版代码

int a[20];  //保存拆分的数位

ll dp[20][state];   //不同题目状态不同

ll dfs(int pos //枚举位置, /*state变量*/  ,bool lead/*前导零*/,  bool limit /*数位上界变量*/)  //不是每个题都要判断前导零
{

//递归边界,既然是按位枚举,最低位是0,那么pos==-1说明这个数我枚举完了
if(pos==-1) return 1;/*这里一般返回1,表示你枚举的这个数是合法的,那么这里就需要你在枚举时必须每一位都要满足题目条件,也就是说当前枚举到pos位,一定要保证前面已经枚举的数位是合法的。不过具体题目不同或者写法不同的话不一定要返回1 */

//第二个就是记忆化(在此前可能不同题目还能有一些剪枝)
if(!limit && !lead && dp[pos][state]!=-1) return dp[pos][state];

//判断是否存在枚举限制
int up=limit?a[pos]:9;

ll ans=0;
//开始计数
for(int i=0;i<=up;i++)
{if() ...
else if()...
ans+=dfs(pos-1,/*状态转移*/,lead && i==0,limit && i==a[pos]) //最后两个变量传参都是这样写的
}

//计算完,记录状态, 不要记录有限制的状态
if(!limit && !lead) dp[pos][state]=ans;
return ans;
}

参考文章:

双方轮流按照最优的决策,判断谁是赢家

这类问题采用 dfs 非常容易解决问题

问题列表

K个逆序对数组

我们用 f(i,j) 表示数字1到i排列中恰好有 j 个逆序对。

在状态转移时, 我们考虑第i 个数字和逆序对个数的关系。

对于第 i 个数, 将其放置到 [1 .. i] 任意一个位置, i 种放置方法

  • 将 i 放到第 i 个位置上, 不会增加 逆序数。
  • 将 i 放到第 i-1 个位置上, 那么会增加1个逆序数
  • 将 i 放到第 i-2 个位置上, 那么会增加2个逆序数

因此将数字 i 放到 [1...i] 的任意一个位置,那么其之后的数形成逆序对。

f(i,j) = f(i-1, j) + f(i-1, j-1) + f(i-1, j-2) + ...+ f(i-1, j-i+1)

此时时间复杂度为 (NNK) , 那么有什么优化的方法吗?