贪心算法:
- 每部最优;
- f(n)=f(n-max(i)+1)
动态规划
- 子结构最优
- f(n)=min{f(n-i1),f(n-i2),f(n-i3)}+1
适用条件:
- 子结构最优
- 无后效性
案例:
-
DAG 有向无环图最短路
image.png
思路:
F(T)=min(F(T.inEdgeTraverse(T)))
代码:
2.最长上升子序列
思路:dp[i]=max{dp[i],max{dp[0:i-1]}+1}
#include<bits/stdc++.h>
int num;
int data[100];
int dp[100];
int DP()
{
for (int i=1;i<=num;i++)
{
for(int j=1;int j<i;j++)
{
if(data[j]<data[i]){ dp[i]=max{dp[i],dp[j]+1};}
}
}
}
int main()
{
cin>>num;
for(int i=0;i<num;i++)
{
cin>>data[i];
dp[i]=1;
}
cout<<DP();
}
算法分析:复杂度为O(n^2)
算法优化:O(nlogn)贪心算法。
- 最长子序和
思路:抛弃负值
#include<bits/stdc++.h>
using namespace std;
int num;
int data[100];
int DP (int *arr) {
int len=num;
int [] dp=new int[len];
dp[0]=arr[0];
int maxSum=arr[0];
for(int i=1;i<len;i++){
dp[i]=Math.max(dp[i-1],0)+arr[i];
maxSum=Math.max(maxSum,dp[i]);
}
return maxSum;
}
void main(){
cin>>num;
for(int i==0;i<num;i++){cin>>data[i];}
Cout<<DP(data);
}
4.编辑距离

image.png
#include<bits/stdc++.h>
using namespace std;
int ans;
public int MinDistance(string word1,string word2){
int len1=word1.length;
int len2=word2.length;
int [][]dp=new int (len1+1,len2+1);
for(int i=0;i<len1;i++){dp[i][0]=i;}
for(int j=0;j<len2;j++){dp[0][j]=j;}
for(int i=1;i<len1;i++){
for(int j=1j<len2;j++){
if(word1.charAt[i-1]==word2.charAt[j-1]) { dp[i][j]=dp[i-1][j-1]; }
else { dp[i][j]=Math.min(dp[i-1][j-1],dp[i][j-1],dp[i-1][j])+1;}
}
}
return dp[len1][len2];
}
- 青蛙上台阶 lc70
转移方程:dp[i]=dp[i-1]+dp[i-2]
#include<bits/stdc++.h>
using namespace std;
int DP(int n){
int [] dp=new int (n+1);
dp[0]=0;
dp[1]=1;
for(int i=2;i<n+1;i++) { dp[i]=dp[i-1]+dp[i-2]; }
return dp[n];
}
- 机器人位移 lc63
int DP(int n,int m){
int [][] dp=new int(n,m);
dp[0][0]=0;
for(int i=0;i<n;i++) {dp[i][0]=1};
foro(int j=0;j<m;j++) { dp[0][j]=1; }
for(int i=1;i<n;i++){
for(int j=1;j<n;j++){
dp[i][j]=dp[i-1][j]+dp[i][j-1];
}
}
return dp[i-1][j-1];
}
-
最长公共子序列 lc300
image.png
int MaxCommonSubSequence(string word1,string word2){
int len1=word1.length;
int len2=word2.length;
int [][]dp=new int (len1+1,len2+1);
dp[0][0]=0;
for (int i=1;i<=len1;i++){
for(int j=1;j<=len2;j++){
if(word1[i-1]==word[j-1]) { dp[i][j]=dp[i-1][j-1]+1; }
else { dp[i][j] = max{dp[i][j-1],dp[i-1][j]; }
}
}
return dp[len1][len2];
}
- 注意:转移方程不可以写成
if(subStr1[-1]==subStr2[-1]){
dp[i][j]=max(dp[i-1][j-1],dp[i][j-1],dp[i-1][j]);
}
反例: acc,ac。dp[i-1][j]=2,dp[i][j]仍为2。
- 最长公共子串
int MaxCommonSubStr(string word1,string word2){
int * lens= new int [2]{word1.length,word2.length};
int [][]dp=new int(lens[0],lens[1]);
int ans=0;
for(int i=0;i<lens[0];i++) {
if(word1[i]== word2[0] ) {
dp[i][0]=1;
}
}
for(int j=0;j<lens[1];j++) {
if(word1[0]== word2[j] ) {
dp[0][j]=1;
}
}
for(int i=1;i<lens[0];i++) {
for(int j=1;j<lens[1];j++){
if(word1[i]==word2[j]) { dp[i][j]=dp[i-1][j-1]+1 ; }
else{dp[i][j]=0;}
ans=Math.max(dp[i][j],ans);
}
}
return ans;
}
- 最长回文子串
思路1:倒置后求最长公共子串
class Solution {
public:
int getLongestPalindrome(string word){
int len=word.length();
int ans=0;
int dp[len][len];
for(int i=0;i<len;i++){
for(int j=0;j<len;j++){
dp[i][j]=0;
}
}
for(int i=0;i<len;i++){ if(word[i]==word[len-1]) dp[i][len-1]=1;}
for(int j=len;j>0;j--){if(word[j-1]==word[0]) dp[0][j-1]=1;}
for(int i=1;i<len;i++){
for(int j=len-2;j>=0;j--){
if(word[i]==word[j]) { dp[i][j] = dp[i-1][j+1]+1 ; }
ans=max(ans,dp[i][j]);
}
}
return ans;
}
};
更容易理解的写法
class Solution {
public:
int getLongestPalindrome(string word){
int len=word.length();
int ans=0;
int dp[len][len];
char str2[len];
for(int i=0;i<len;i++){
for(int j=0;j<len;j++){
dp[i][j]=0;
}
}
int i=len-1;
for(int k=0;i>=0;i--){str2[k++]=word[i];}
for(int i=0;i<len;i++){ if(word[i]==str2[0]) dp[i][0]=1;}
for(int j=0;j<len;j++){if(str2[j]==word[0]) dp[0][j]=1;}
for(int i=1;i<len;i++){
for(int j=1;j<len;j++){
if(word[i]==str2[j]) { dp[i][j] = dp[i-1][j-1]+1 ; }
ans=max(ans,dp[i][j]);
}
}
return ans;
}
};
三思后发现这种算法其实不可取,如出现"abcdecacdcba"的字符串时,算法会将abcd当成回文
正常的DP思路:
d[i][j]=d[i+1][j-1]&&s[i]==s[j]?true:false;
class Solution {
public:
int getLongestPalindrome(string word){
int len=word.length();
if(len==0)return 0;
int ans=1;
bool dp[len][len];
for(int i=0;i<len;i++){
for(int j=0;j<len;j++){
dp[i][j]=false;
}
}
for(int i=0;i<len;i++){
dp[i][i]=true;
if(i<len-1&&word[i]==word[i+1]) {
dp[i][i+1]=true;
ans=2;
}
}
for(int l=3;l<=len;l++){
for(int i=0;i<len-l+1;i++){
int r=l+i-1;
if(word[i]==word[r]&&dp[i+1][r-1]){
dp[i][r]=true;
ans=max(ans,l);
}
}
}
return ans;
}
};
Tips:
-
双重循环的外循环必须为l,因为dp表为:
image.png
转移方程为dp[i][j]<--dp[i+1][j-1];(j>=i)
先对dp[i][i]赋值,ans=1
由于可以通过dp[i][i]->dp[i][i+1],可先初始化两个对角线,ans=2
(注意ans=2的递推和普遍公式(ans>=3)不一致),而这两部转移的复杂度是O(n),不怎么影响时间。
for(int i=0;i<len;i++){
dp[i][i]=true;
if(i<len-1&&word[i]==word[i+1]) {
dp[i][i+1]=true;
ans=2;
}
}
再通过一般转移方程得到最终答案。
- 中心扩展思路
class Solution {
public:
int getLongestPalindrome(string word){
int ans=0;
if(word.size()<2){return word.size();}
if(word.size()==2){return word[0]==word[1]?2:1;}
for(int i=1;i<word.size()-1;i++){
int len1=GetMaxLen(word, i, i);
int len2=GetMaxLen(word, i, i+1);
if(i==1){cout<<len2<<endl;}
ans=max(ans,max(len1,len2));
}
return ans;
}
int GetMaxLen(string s,int l,int r){
while(l>=0&&(r<s.size())&&s[l]==s[r]){
l=l-1;
r=r+1;
}
return r-l-1;
}
};
- 单词拆分
class Solution {
public:
bool wordBreak(string s, unordered_set<string> &dict) {
int len = s.length();
bool dp[len+1];
for(int i=1;i<=len;i++){dp[i]=false;}
dp[0]=true;
for(int i = 1;i <= len; i++)
{
for(int j = i-1; j >= 0; j--)
{
if(dp[j] && dict.find(s.substr(j,i-j))!= dict.end())
{
dp[i] = true;
break;
}
}
}
return dp[len];
}
};
另一种写法:
思路:
- 若中间出现非字典单词,则无需向后判断
- 可从每个位置的字符串出发,扫描是否存在字典单词,若存在则在字符串中的该单词末尾记为true;
- 若字符串结尾字符被记为true,则该字符串可拆分。
-
转移方程:
image.png
class Solution {
public:
bool wordBreak(string s, unordered_set<string> &dict) {
int len = s.length();
vector<bool>dp(len+1,false);
dp[0]=true;
for(int i = 0;i < len; i++)
{
for (auto word = dict.begin(); word != dict.end(); ++word)
{
int wordlen=(*word).length();
if(i>=wordlen-1 && dp[i-wordlen+1] && s.substr(i-wordlen+1,wordlen)==*word)
{
dp[i+1]=true;
}
}
}
return dp[len];
}
};
知识点
- unordered_set的find:返回的是unordered_set::iterator类型
dict.find(s.substr(j,i-j))!= dict.end()
- unordered_set的traverse:(iterator可以自增)
for (auto word = dict.begin(); word != dict.end(); ++word)
或
for (unordered_set::iterator word = dict.begin(); word != dict.end(); ++word)
- vector创建定长数组:
vector<type>name(size,初始值);
- string.substr(pos,len)
- 三角最小路径
class Solution {
public:
int minTrace(vector<vector<int> >& triangle) {
// write code here
int len=0;
for(int i=0;i<triangle.size();i++){
for(int j=0;j<triangle[i].size();j++){len++;}
}
vector<int>dp(len,0);
dp[0]=triangle[0][0];
int start=0;
int end=0;
for(int i=1;i<triangle.size();i++){
start+=triangle[i-1].size();
end=start+triangle[i].size()-1;
dp[start]=triangle[i][0]+dp[start-triangle[i-1].size()];
dp[end]=triangle[i][triangle[i].size()-1]+dp[end-triangle[i].size()];
}
if(len>1){start=1;}
for(int i=2;i<triangle.size();i++){
start +=triangle[i-1].size();
for(int j=1;j<triangle[i].size()-1;j++){
dp[start+j]=triangle[i][j]+min(dp[start+j-triangle[i].size()],dp[start+j-triangle[i].size()+1]);
}
}
int minLen=2147483647;
for(int i=0;i<triangle[triangle.size()-1].size();i++){
minLen=min(minLen,dp[start+i]);
}
return minLen;
}
};
- 最大正方形 lc221
class Solution {
public:
int solve(vector<vector<char> >& matrix) {
// write code here
bool dp[matrix.size()][matrix[0].size()];
int ans=0;
for(int i=0;i<matrix.size();i++) { dp[i][0]=matrix[i][0]=='1'?1:0; }
for(int j=0;j<matrix[0].size();j++) { dp[0][j]=matrix[0][j]=='1'?1:0; }
for(int i=1;i<matrix.size();i++){
for(int j=1;j<matrix[0].size();j++){
if(matrix[i][j]=='0'||matrix[i-1][j]=='0'||matrix[i][j-1]=='0'||matrix[i-1][j-1]=='0'){
dp[i][j]=0;
}
else{
dp[i][j]=min(min(dp[i-1][j-1],dp[i-1][j]),min(dp[i][j-1],dp[i-1][j]))+1;
ans=dp[i][j]>ans?dp[i][j]:ans;
}
}
}
return ans*ans;
}
};
- 矩阵检索 lc304
- 暴力解法
双循环累加
-
前缀和优化
转移方程:
image.png
代码:
class NumMatrix {
int[][] dp;
public NumMatrix(int[][] matrix) {‘
dp[i][j]的值为(0,0)到(i,j)的面积
if(matrix.length==0||matrix[0].length==0)
return;
int m=matrix.length;
int n=matrix[0].length;
dp=new int[m+1][n+1];
dp[0][0]=matrix[0][0];
for(int i=1;i<m;i++)
dp[i][0]=dp[i-1][0]+matrix[i][0];
for(int j=1;j<n;j++)
dp[0][j]=dp[0][j-1]+matrix[0][j];
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+matrix[i][j];
}
}
}
public int sumRegion(int row1, int col1, int row2, int col2) {
按照转移方程加加减减,注意边界条件
if(row1==0){
if(col1==0){
return dp[row2][col2];
}else{
return dp[row2][col2]-dp[row2][col1-1];
}
}else{
if(col1==0)
return dp[row2][col2]-dp[row1-1][col2];
else
return dp[row2][col2]-dp[row1-1][col2]-dp[row2][col1-1]+dp[row1-1][col1-1];
}
}
}
- 零钱兑换 lc322
class Solution {
public:
int minMoney(vector<int>& arr, int aim) {
// write code here
int dp[aim+1];
dp[0]=0;
if(aim>0){dp[aim]=-1;}
int minValue;
for(int i=0;i<arr.size();i++){if(aim>=arr[i]) dp[arr[i]]=1;}
for(int i=1;i<=aim;i++){
minValue=2147483647;
for(int j=0;j<arr.size();j++){
if(i>=arr[j]&&dp[i-arr[j]]>-1){
minValue=min(minValue,dp[i-arr[j]]);
}
if(minValue!=2147483647){ dp[i]=minValue+1;}
else{dp[i]=-1;}
}
}
return dp[aim];
}
};
15.Floyd算法:
void Floyd(MGraph G[M][N],int Path[M][N])
{
int A[M][N];
int Path[M][N];
for(int i=0;i<M;i++){
for(int j=0;j<N;j++){
A[i][j]=G[i][j];
Patj[i][j]=-1;
}
}
for(int v=0;v<VertexNum;v++)
{
for(int i=0;i<M;i++)
{
for(int j=0;j<N;j++)
{
if(A[i][v]+A[v][j]<A[i][j])
{
A[i][j]=A[i][v]+A[v][j];
Path[i][j]=v;
}
}
}
}
}




