动态规划是分治思想的延伸,大事化小小事化无
动态规划具备的特点:
1.把原来的问题分解成几个相似的子问题
2.所有的子问题都只需要解决一次
3.存储子问题的解
动态规划的本质,是对问题状态的定义和状态转移方程的定义
从以下角度考虑:
1.状态定义(定义的状态一定要形成递推关系)
2.状态间的转移方程定义
3.状态的初始化
4.返回结果
最大值/最小值,可不可行,是不是,方案个数
LeetCode:斐波那契数列
分析问题过程:
问题:数列第N项的值
状态F(i):数列第i项的值
转移方程:F(i)=F(i-1)+F(i-2)
初始状态:F(0)=0,F(1)=1
返回:F(n)
class Solution {
public:
//递归
int fib(int n) {
if(n<=1){
return n;
}
if(n==2){
return 1;
}
return fib(n-1)%1000000007+fib(n-2)%1000000007;
}
//动态规划-记录每个子问题的解
int fib(int n){
if(n==0){
return 0;
}
vector<int> v(n+1,0);
v[1]=1;//初使状态
for(int i=2;i<=n;i++){
v[i]=v[i-1]%1000000007+v[i-2]%1000000007;//转移方程
}
return v[n]%1000000007;
}
//动态规划-不记录每个子问题解
int fib(int n){
if(n<=1){
return n;
}
int a=0;
int b=1;
int num;
for(int i=2;i<=n;i++){
num=a%1000000007+b%1000000007;
a=b;
b=num;
}
return num%1000000007;
}
};
牛客:变态跳台阶
问题:跳上n级台阶的方法个数
状态F(i):跳上i级台阶的方法个数
转移方程:F(i)=F(0)+F(1)+F(2)+…+F(i-1);
F(i-1):F(i-2)+F(i-3)+…+F(0);
F(i)=F(i-1)+F(i-1)=2*F(i-1);
初使状态:F(1)=1
返回:F(n)
class Solution {
public:
int jumpFloorII(int number) {
if(number==0)
return 0;
int ret=1;
for(int i=2;i<=number;i++){
ret=ret*2;//转移方程
}
return ret;
}
};
状态转移方程:F(i)=F(i-1)+F(i-2)
牛客:矩形覆盖
状态转移方程:F(i)=F(i-1)+F(i-2)
class Solution {
public:
int rectCover(int number) {
if(number<=1){
return number;
}
int a=1;
int b=1;
int ret;
for(int i=2;i<=number;i++){
ret=a+b;
a=b;
b=ret;
}
return ret;
}
};
问题:数组的最大连续和
状态:F(i)
转移方程:F(i)=max(F(i-1)+a[i],a[i])
初使状态F(0)=a[0]
返回值:max(F(i))
class Solution {
public:
int FindGreatestSumOfSubArray(vector<int> array) {
if(array.empty()){
return 0;
}
vector<int> v(array.size(),0);
v[0]=array[0];
int ret=v[0];
for(int i=1;i<array.size();i++){
v[i]=max(v[i-1]+array[i],array[i]);//状态转移方程
ret=max(ret,v[i]);
}
return ret;
}
};
问题:单词是否可以成功分割
状态:F(i)
转移方程:F(i):j 返回结果:F(s.size())
class Solution {
public:
/*bool wordBreak(string s, unordered_set &dict) {
vector v(s.size() + 1);
for (int i = 1; i <= s.size(); i++){
if (dict.find(s.substr(0, i)) != dict.end()){
v[i] = true;
continue;
}
for (int j = i - 1; j>0; j--){
if (v[j] && (dict.find(s.substr(j, i)) != dict.end())){
v[i] = true;
break;
}
}
}
return v[s.size()];
}*/
bool wordBreak(string s, unordered_set<string> &dict) {
vector<bool> v(s.size() + 1);
v[0] = true;
for (int i = 1; i <= s.size(); i++){
for (int j = i - 1; j>=0; j--){
if (v[j] && (dict.find(s.substr(j, i)) != dict.end())){
v[i] = true;
break;
}
}
}
return v[s.size()];
}
};
牛客:三角矩阵
问题:从(0,0)到达最后一行的最小路径和
状态F(i,j):从(0,0)到达(i,j)的最小路径和
F(i,j):min(F(i-1,j),F(i-1,j-1))+a[i][j]
每一行第一列,最后一列只有一条路到达
F(i,0):F(i-1,0)+a[i][0]
F(i,i):F(i-1,i-1)+a[i][i]
初使状态:F(0,0)=a[0][0]
返回结果:max{F(row-1,j)}
class Solution {
public:
int minimumTotal(vector<vector<int> > &triangle) {
if(triangle.empty()){
return 0;
}
vector<vector<int>> vv(triangle);
int row=triangle.size();
int col=triangle[0].size();
for(int i=1;i<row;i++){
for(int j=0;j<i+1;j++){
if(j==0){
vv[i][j]=vv[i-1][j]+triangle[i][j];
}
else if(j==i){
vv[i][j]=vv[i-1][j-1]+triangle[i][j];
}
else{
vv[i][j]=min(vv[i-1][j-1],vv[i-1][j])+triangle[i][j];
}
}
}
int row_vv=row-1;
int ret=vv[row_vv][0];
for(int i=0;i<vv[row_vv].size();i++){
ret=min(ret,vv[row_vv][i]);
}
return ret;
}
};
LeetCode:不同路径
问题:从(0,0)到达(i,j)的路径和
状态F(i,j):从(0,0)到达(i,j)的路径个数
转移方程:F(i,j)=F(i-1,j)+F(i,j-1)
初使状态:F(0,0)=1
返回结果:F(i,j)
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>> vv(m,vector<int>(n));
for(int i=0,j=0;j<n;j++){
vv[i][j]=1;
}
for(int i=0,j=0;i<m;i++){
vv[i][j]=1;
}
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
if(i-1<0){
vv[i][j]=vv[i][j-1];
}
else if(j-1<0){
vv[i][j]=vv[i-1][j];
}
else{
vv[i][j]=vv[i-1][j]+vv[i][j-1];
}
}
}
return vv[m-1][n-1];
}
};
LeetCode:不同路径II
状态F(i,j):从(0,0)到(i,j)路径个数
F(i,j):
if(obstacle[i][j]==1)
F(i,j)=0
else
F(i,j)=F(i,j-1)+F(i-1,j)
初使状态:
if(obstacle[i][0]==0&&obstacle[j][0]==0&&j F(i,0)=1;
if(obstacle[0][i]==0&&obstacle[0][j]==0&&j F(0,i)=1;
class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
if(obstacleGrid.empty()){
return 0;
}
int row=obstacleGrid.size();
int col=obstacleGrid[0].size();
vector<vector<int>> vv(row,vector<int>(col,0));
for(int i=0;i<row;i++){
if(obstacleGrid[i][0]==0){
vv[i][0]=1;
}
else{
break;
}
}
for(int i=0;i<col;i++){
if(obstacleGrid[0][i]==0){
vv[0][i]=1;
}
else{
break;
}
}
for(int i=1;i<row;i++){
for(int j=1;j<col;j++){
if(obstacleGrid[i][j]==1){
vv[i][j]=0;
}else{
vv[i][j]=vv[i-1][j]+vv[i][j-1];
}
}
}
return vv[row-1][col-1];
}
};
牛客:带权值的最小路径和
状态F(i,j)
状态方程:F(i,j)=min(F(i-1,j),F(i,j-1))+F(i,j)
初使值:F(0,0)=grid[0][0]
返回值:F(row-1,col-1)
class Solution {
public:
/**
*
* @param grid int整型vector>
* @return int整型
*/
int minPathSum(vector<vector<int> >& grid) {
// write code here
int row=grid.size();
int col=grid[0].size();
vector<vector<int>> vv(grid);
for(int i=1;i<row;i++){
vv[i][0]=vv[i-1][0]+vv[i][0];
}
for(int j=1;j<col;j++){
vv[0][j]=vv[0][j-1]+vv[0][j];
}
for(int i=1;i<row;i++){
for(int j=1;j<col;j++){
vv[i][j]=min(vv[i-1][j],vv[i][j-1])+vv[i][j];
}
}
return vv[row-1][col-1];
}
};
class Solution {
public:
/**
* @param m: An integer m denotes the size of a backpack
* @param A: Given n items with size A[i]
* @param V: Given n items with value V[i]
* @return: The maximum value
*/
int backPackII(int m, vector<int> &A, vector<int> &V) {
// write your code here
int n=A.size();
if(n==0||m==0)
return 0;
vector<vector<int>> maxV(n+1,vector<int>(m+1,0));
for(int i=1;i<=n;i++){
for(int j=1;j<=m;++j){
if(A[i-1]<=j){
maxV[i][j]=max(maxV[i-1][j],maxV[i-1][j-A[i-1]]+V[i-1]);
}
else{
maxV[i][j]=maxV[i-1][j];
}
}
}
return maxV[n][m];
}
};
class Solution {
public:
/**
*
* @param s string字符串
* @return int整型
*/
bool ispal(string s,int start,int end){
while(start<end){
if(s[start]!=s[end])
return false;
++start;
--end;
}
return true;
}
int minCut(string s) {
vector<int> v(s.size()+1);
for(int i=1;i<=s.size();i++){
v[i]=i-1;
}
for(int i=2;i<=s.size();i++){
if(ispal(s,0,i-1)){
v[i]=0;
continue;
}
for(int j=1;j<i;j++){
if(ispal(s,j,i-1)){
v[i]=min(v[i],v[j]+1);
}
}
}
return v[s.size()];
}
};
编辑距离
问题:word1转成word2的最小操作次数
状态F(i,j):word1前i个字符转成Word2的前j个字符的最小操作次数
转移方程:F(i,j):min(F(i-1,j-1),F(i-1,j),F(i,j-1))+1
class Solution {
public:
/**
*
* @param word1 string字符串
* @param word2 string字符串
* @return int整型
*/
int minDistance(string word1, string word2) {
// write code here
int len1=word1.length();
int len2=word2.length();
vector<vector<int>> minD(len1+1,vector<int>(len2+1));
for(int i=0;i<=len2;++i)
minD[0][i]=i;
for(int i=0;i<=len1;++i){
minD[i][0]=i;
}
for(int i=1;i<=len1;i++){
for(int j=1;j<=len2;++j){
minD[i][j]=min(minD[i][j-1],minD[i-1][j])+1;
if(word1[i-1]==word2[j-1])
minD[i][j]=min(minD[i][j],minD[i-1][j-1]);
else
minD[i][j]=min(minD[i][j],minD[i-1][j-1]+1);
}
}
return minD[len1][len2];
}
};