1.最长上升子序列
这道题和最长抖动序列(花匠)问题的区别在于 抖动序列每次递推 都为dp表保存了两个值 ,一个是上升表一个是下降表 这其中 无论当前值与前一个值的大小关系 都包含了取与不取的关系 也就是 这个表下来 包含了取任意多少的元素情况 因此最后结果直接cout即可
但是这道上升子序列问题 首先只有一个变量 就是当前位置 其次它只考虑了取当前值的问题 如果不取当前值(也就是数字未递增)就没有进行更新新递归表 所以它需要在指针走到每一位置 取一下最大值max 最终输出max而不是dp的终止值
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n=nums.size();
if(n==0)return 0;
vector<int>dp(n,1);
int res=1;
for(int i=1;i<n;i++){
for(int j=0;j<i;j++){
if(nums[i]>nums[j]){
dp[i]=max(dp[i],dp[j]+1);
}
}
res=max(res,dp[i]);
}
return res;
}
};
试过了还是要按照这个来
1.1 最大子序和也要经过随时记录最大值
因为就算没有小于0 它也会比以前小 随时记录最值 跑不了
2.分割等和子集
这道题本来第一眼是打算用dfs 写的 两分钟就能写出来
class Solution {
public:
bool flag=false;
bool canPartition(vector<int>& nums) {
dfs(0,0,0,nums);
return flag;
}
void dfs(int a,int b,int cur,vector<int>&nums){
if(cur==nums.size()){
if(a==b){
flag=true;
}
return;
}
dfs(a+nums[cur],b,cur+1,nums);
dfs(a,b+nums[cur],cur+1,nums);
}
};
但是发现超出时间限制了
只能作罢
方法是把它转为背包问题上来
如果可以等分 那么两个子集每一个都是sum/2
所以把这个数设为target 其次是 对于动态规划背包 这次dp不是求最多 而是能否 bool值dp
初始化时全部设为false 只把dp[0][0]为true 即可 然后就是 这个的递推公式是 用 || 而不是+1了
注意下标
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum=0;
for(int i=0;i<nums.size();i++){
sum+=nums[i];
}
if(sum%2!=0)return false;
int target=sum/2;
bool dp[nums.size()+1][target+1];
int bag=0;
for(int i=0;i<=nums.size();i++)
for(int j=0;j<=target;j++){
dp[i][j]=false;
}
for(int i=0;i<=nums.size();i++){
dp[i][0]=true;
}
dp[0][0]=true;
for(int i=1;i<=nums.size();i++){
for(int j=1;j<=target;j++){
if(nums[i-1]>j){
dp[i][j]=dp[i-1][j];
}
else{
dp[i][j]=dp[i-1][j] ||dp[i-1][j-nums[i-1]];
}
}
}
return dp[nums.size()][target];
}
};
其中赋值true循环可以删除
3.零钱兑换II
要注意:!!!临时变量自动初始化是随机值 所以定义变量要么在函数外开头全局变量 要么就临时变量一定要进行人为初始化
这道题和零钱兑换I的区别在于 要找最小金币数量 那么要对于每一个金额 都用所有的金币去试试 看谁最少 所以外循环是金额 内循环是金币
这道题是求所有可能的方法数 那么就从金币出发 对现有的每一个金币 去试试它所有能凑成的金额传递
所以外循环是金币 内循环是金额
class Solution {
public:
int change(int amount, vector<int>& coins) {
vector<int>dp(amount+1,0);
dp[0]=1;
for(auto coin:coins){
for(int cnt=coin;cnt<=amount;cnt++){
dp[cnt]+=dp[cnt-coin];
}
}
return dp[amount];
}
};