DP一般题目:最值问题/计数问题/存在性问题
现在有给定面值的硬币,问你~~最少~~ 可以用几枚硬币组合成 n 元钱,所有面值的硬币无限多。(下面用现有2、5、7面值的硬币与凑27元为例解释)
#include
#include
#include
using namespace std;
#define INF 0x3fffffff
int main()
{
vector<int> a; //现有面值放在一个数组
int kind=a.size(); //已知面值种类数量
int n; //要凑的面值大小
cin>>n;
int f[n+1]; //用0至n,数组开n+1
f[0]=0; //初始化
for(int i=1;i<=n;i++)
{
f[i]=INF;
for(int j=0;j<kind;j++)
{
if(i>=a[j]&&f[i-a[j]]!=INF)
f[i]=min(f[i-a[j]]+1,f[i]);
}
}
if(f[n]==INF)
f[n]=-1;
cout<<f[n]<<endl;
}
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有~~多少种不同的方法~~ 可以爬到楼顶呢?
注意:给定 n 是一个正整数。
#include
using namespace std;
int main()
{
int n; //n阶楼梯
cin>>n;
int f[n+1]; //利用0...n,数组开到n+1
f[0]=f[1]=1;
for(int i=2;i<n+1;i++)
{
f[i]=f[i-1]+f[i-2];
}
cout<<f[n]<<endl;
}
设有一个三角形的数塔,顶点结点称为根结点,每个结点有一个整数数值。从顶点出发,在每一个结点可以选择向左走或者向右走一直走到底层,要求找出一条路径,使路径上的值~~最大~~。
example(示意图,内部存储依然整齐存储):
13
11 8
12 7 26
6 14 15 8
12 7 13 24 11
Sample Output
max=86
#include
#include
using namespace std;
int main()
{
int a[105][105],f[105][105];
int n;
cin>>n;
for(int i=0;i<n;i++)
{
for(int j=0;j<=i;j++)
cin>>a[i][j];
}
for(int i=0;i<n;i++)
f[n-1][i]=a[n-1][i]; //先将最后一行存入到f数组中
for(int i=n-1;i>=0;i--)
{
for(int j=0;j<=i;j++)
f[i][j]=max(f[i+1][j],f[i+1][j+1])+a[i][j];
}
cout<<f[0][0];
}
机器人位于m x n 网格的左上角(下图中标记为"开始")。机器人只能在任何时间点向下或右转。机器人试图到达网格的右下角(下图中标有"完成")。~~有多少~~可能的唯一路径?
横坐标为(0…m-1)左上角(0,0)
纵坐标为(0…n-1) 右下角(m-1,n-1)
#include
using namespace std;
int main()
{
int m,n;
cin>>m>>n;
int f[m][n];
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(i==0||j==0)
f[i][j]=1;
else
f[i][j]=f[i-1][j]+f[i][j-1];
}
}
cout<<f[m-1][n-1]<<endl;
}
给定一个非负整数数组a[],青蛙最初定位在数组的第一个索引处即石头0处。数组中的每个元素表示该位置的最大跳转长度即青蛙在石头i处最大可向右跳的距离为ai。确定青蛙~~是否能够~~到达最后一个索引即跳到最后一个石头n-1上。
#include
using namespace std;
int main()
{
int n; //有多少石头
cin>>n;
int a[n],f[n];
for(int i=0;i<n;i++)
cin>>a[i];
f[0]=true;
for(int x=1;x<n;x++)
{
f[x]=false;
for(int i=0;i<x;i++)
{
if(f[i]&&i+a[i]>=x)
{
f[x]=true;
break;
}
}
}
cout<<f[n-1]<<endl;
}
给定字符串 s 和 t ,判断 s ~~是否~~为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(如"ace"是"abcde"的一个子序列,而"aec"不是)。
暴力算法:直接对比 O(n)
class Solution {
public:
bool isSubsequence(string s, string t)
{
int sLen = s.length();
int tLen = t.length();
if (sLen <= 0)
return true;
else if (sLen > tLen)
return false;
int ss = 0;
for (int i = 0; i < tLen; i++)
{
if (s[ss] == t[i])
{
ss++;
if (ss == sLen)
{
return true;
}
}
}
return false;
}
};
动态规划: O(n 2 ^{2} 2)
状态转换示意图
练习演示推导链接:https://alchemist-al.com/algorithms/is-subsequence
class Solution {
public:
bool isSubsequence(string s, string t) {
int m=s.length();
int n=t.length();
if (m <= 0)
return true;
if (m > n)
return false;
bool f[m+1][n+1];
for(int i=0;i<=m;i++)
{
for(int j=0;j<=n;j++)
{
if(i==0) f[i][j]=true;
else if(j==0) f[i][j]=false;
else if(i==0&&j==0) f[i][j]=true;
else
{
if(f[i][j-1]) f[i][j]=true;
else if(s[i-1]==t[j-1]&&f[i-1][j-1])
f[i][j]=true;
else f[i][j]=false;
}
}
}
return f[m][n];
}
};
给定n个数,求这n个数的~~最长~~上升子序列的长度。什么是最长上升子序列? 就是给你一个序列,请你在其中求出一段不断严格上升的部分,它不一定要连续。就像这样:2,3,4,7和2,3,4,6就是序列2 5 3 4 1 7 6的两种选取方案。最长的长度是4。
class Solution {
public:
int LIS(vector<int> a)
{
int n=a.size();
int f[n];
f[0]=1;
for(int i=1;i<n;i++)
//每次求以第i个数为终点的最长上升子序列的长度
{
int temp=0; //记录满足条件的,第i个数左边的上升子序列的最大长度
for(int j=0;j<i;j++)
{ //查看以第j个数为终点的最长上升子序列
if(a[i]>a[j])
{
if(temp<f[j])
temp=f[j];
}
}
f[i]=temp+1;
}
int result;
for(int i=0;i<n-1;i++)
result=max(f[i],f[i+1]);
return result;
}
};
给定序列长度为m的字符串s,长度为n的字符串t求s,t的~~最长~~公共子序列长度。例如,{1,3,4,5,6,7,7,8},{3,5,7,4,8,6,7,8,2}的最长公共子序列长度为5。
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
int m=text1.length();
int n=text2.length();
int f[m+1][n+1]; //多用一个空字符串的空间
for(int i=0;i<=m;i++)
{
for(int j=0;j<=n;j++)
{
if(i==0||j==0) f[i][j]=0;
else
{
if(text1[i-1]==text2[j-1])
f[i][j]=f[i-1][j-1]+1;
else
f[i][j]=max(f[i-1][j],f[i][j-1]);
}
}
}
return f[m][n];
}
};
"选不选"策略
给定一个整数数组 nums ,找到一个具有~~最大~~和的连续子数组(子数组最少包含一个元素),求其最大和。
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int l=nums.size();
int f[l],maxsum=nums[0]; //保存最大连续子序列的和
f[0]=nums[0];
for(int i=1;i<l;i++)
{
f[i]=max(f[i-1]+nums[i],nums[i]);
maxsum=max(maxsum,f[i]);
}
return maxsum;
}
};
一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列nums,替按摩师找到最优的预约集合(总预约时间~~最长~~),返回总的分钟数。
class Solution {
public:
int massage(vector<int>& nums) {
int n=nums.size();
if(n == 0) return 0;
if(n == 1) return nums[0];
int f[n];
f[0]=nums[0];
f[1]=max(nums[0],nums[1]);
for(int i=2;i<n;i++)
{
f[i]=max(f[i-2]+nums[i],f[i-1]);
}
return f[n-1];
}
};
给定一组数arr(如:3, 34, 4, 12, 5, 2)和S(如:9),若~~能~~从所给的数中,选出若干个数使它们的和为S,则输出true,否则输出false。
状态示意图
i\j | 0 1 2 3 4 5 6 7 8 9 |
---|---|
3 | |
34 | |
4 | |
12 | |
5 | |
2 |
class Solution {
public:
bool func(vector<int>& arr,int S)
{
int n=arr.size();
bool f[n][S+1];
for(int i=0;i<n;i++)
f[i][0]=true;
for(int j=0;j<=S;j++)
{
if (arr[0]==j)
f[0][j]=true;
else f[0][j]=false;
}
for(int i=1;i<n;i++)
{
for(int j=1;j<=S;j++)
{
if(arr[i]>j) //已经凑得与前面状态相同
f[i][j]=f[i-1][j];
else
f[i][j]=f[i-1][j]|f[i-1][j-arr[i]];
}
}
return f[n-1][S];
}
};