动态规划
记忆化搜索
1.记忆化搜索必须满足动态规划从初始状态到最终状态
2.记忆化搜索中,搜索相当于“标记访问”
leetcode 329 矩阵中的最长递增路径和:
1.每个点从大往小搜
2.每个点从小往大搜
3.上述都可以
leetcode 354 俄罗斯套娃问题:
1.二维判断
方法1:记忆化搜索
int medfs(int index, vector<vector<int>> &nums)
{
// cout<<"i: "<<index<<endl;
if (dp[index]!=0)
return dp[index];
int curmax=0;
for (int i=0; i<nums.size(); i++)
{
if (nums[i][0]<nums[index][0] && nums[i][1]<nums[index][1])
{
curmax=max(curmax, medfs(i, nums));
}
}
// cout<<"dp: "<<dp[index]+1<<endl;
return dp[index]=curmax+1;//注意赋值
}
int maxEnvelopes(vector<vector<int>>& nums)
{
if (nums.size()==0)
return 0;
memset(dp, 0, sizeof(dp));
int MAX=-INF;
for (int i=0; i<nums.size(); i++)
{
if (!dp[i])
{
MAX = max(MAX, medfs(i, nums));
// cout<<"zong i "<<i<<" "<<dp[i]<<"***********************"<<endl;
}
}
// cout<<"dp3"<<dp[3]<<endl;
return MAX;
}
方法2:自底向上
static bool cmp(vector<int> &a, vector<int> &b)
{
if (a[0]!=b[0])
return a[0]<b[0];
else
return a[1]<b[1];
}
int maxEnvelopes(vector<vector<int>>& nums)
{
if (nums.size()==0)
return 0;
fill(dp, dp+nums.size(), 1);
int MAX=-INF;
sort(nums.begin(), nums.end(), cmp);
for (int i=0; i<nums.size(); i++)
{
for (int j=0; j<nums.size(); j++)
{
if (j != i && nums[j][0]>nums[i][0] && nums[j][1]>nums[i][1])
{
dp[j] = max(dp[j], dp[i]+1);
}
}
if (dp[i] > MAX)
{
MAX=dp[i];
}
}
return MAX;
}
leetcode 174 地下城问题:
1.在记忆化搜索的过程中,需要状态不只依赖后续前状态,还需对状态判断,做手动处理
int medfs(int i, int j, int m, int n, vector<vector<int>> &dungeon, int dp[maxn][maxn]) //记忆化搜索
{
if (i==m-1 && j==n-1)//目标点
return dungeon[i][j];
if (dp[i][j]!=0)//0不能单纯的做标记(图中存在0结点)
return dp[i][j];
int curmax=-INF;//最大负数
for (int k=0; k<2; k++)
{
int newi=i+dir[k][0];
int newj=j+dir[k][1];
if (0<=newi && newi<m && 0<=newj && newj<n)
{
curmax = max(curmax, medfs(newi, newj, m, n, dungeon, dp));
}
}
// cout<<i<<" "<<j<<": "<<curmax<<endl;
curmax = curmax>0 ? 0:curmax;
return dp[i][j] = dungeon[i][j] + curmax;
}
//主函数中返回的额外判断
if (dp[0][0]<0)
return abs(dp[0][0])+1;
else
return 1;
leetcode 377 组合总和4:
1.使用记忆化搜索(复杂错误思想->正确思想)
错误思想:
//错误: 简单问题复杂化
//记忆化搜索
//dp是bool变量
const int maxn=10005;
int dp[maxn];
int visited[maxn];//增加标记访问
class Solution {
public:
int dfs(vector<int>& nums, int target)
{
if (target==0)//边界
return 0;
//记忆化
if (dp[target]==0 && visited[target])
return 0;
if (dp[target]!=0 && dp[target]!=1)
return dp[target];
for (int i=0; i<nums.size(); i++)
{
if (target>=nums[i])
{
dp[target]+=dfs(nums, target-nums[i]);
}
}
visited[target]=1;
return dp[target];
}
int combinationSum4(vector<int>& nums, int target)
{
// 0%1 == 0;
if (nums.size()==0)
return 0;
//init
memset(dp, 0, sizeof(dp));
memset(visited, 0, sizeof(visited));
for (int i=0; i<nums.size(); i++)
{
dp[nums[i]]=1;
}
int ans = dfs(nums, target);
return ans;
}
};
正确思想:
//记忆化搜索
//dp是bool变量
const int maxn=10005;
int dp[maxn];
int visited[maxn];//增加标记访问
class Solution {
public:
int dfs(vector<int>& nums, int target)
{
if (target==0)//边界//能达到边界就是一个结果
return 1;
//记忆化
if (visited[target])
return dp[target];
for (int i=0; i<nums.size(); i++)
{
if (nums[i]<=target)
{
dp[target]+=dfs(nums, target-nums[i]);
}
}
visited[target]=1;
return dp[target];//如果都填不上会返回0
}
int combinationSum4(vector<int>& nums, int target)
{
if (nums.size()==0)
return 0;
//init
memset(dp, 0, sizeof(dp));
memset(visited, 0, sizeof(visited));
int ans = dfs(nums, target);
return ans;
}
};
2019.10
leetcode 279 完全平方数:
1.如何从暴力搜索(重叠子问题)->记忆化->自底向上动态规划
2.每个状态使用可能使用序列之前数字的状态->二重循环
3.初,使用了完全背包,有很大的冗余(但是本题类似于完全背包)
int numSquares(int n)
{
int dp[maxn];
dp[0]=0;
for (int i=1; i<=n; i++)
{
dp[i]=i;//初始化(1也是完全平方数)
for (int j=1; i-j*j>=0; j++)
{
dp[i]=min(dp[i], dp[i-j*j]+1);//+1体现了动态规划自底向上
}
}
return dp[n];
}