总
动态规划的精髓在于拆分问题,使问题能够用分治解决
递归求解
1413
打卡题,最简单的递归
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
long long a[100];
a[1]=1,a[2]=2;
while(cin>>n)
{
for(int i=3;i<=n;i++)
a[i]=a[i-1]+a[i-2];
cout<<a[n]<<endl;
}
return 0;
}
1197
与上题一模一样,要能看出来
1033
简单找规律
最大子段和
1172
每段都从前往后累加,出现负值就放弃,重新开始
#include<iostream>
#include<string>
#include<string.h>
#include<vector>
#include<stdio.h>
#include <queue>
#include <functional>
#include<algorithm>
using namespace std;
const int maxn=1e6+5;
long long a[maxn],b[maxn];
long long maxx;
int main()
{
int n;
while(cin>>n)
{
for(int i=0;i<n;i++)
cin>>a[i];
b[0]=a[0];
maxx=a[0];
for(int i=1;i<n;i++)
{
b[i]=max(b[i-1]+a[i],a[i]);
if(maxx<b[i])
maxx=b[i];
}
cout<<maxx<<endl;
}
return 0;
}
1642
最大子段和的变形
这个题如果看了对应的动态规划的视频或者n诺的教程,这个题的原理应该不难理解。
主要是有两个细节,这里我最开始考虑的是把输入的字符0换做-1,字符1就是1,为了统计数量方便,和最大字段和的思路一样。
后来发现没有必要,这里是动态规划的思想,没必要强行去套格式,主要是理解到这里的核心是这里的递推或者说迭代更新,不需要强行去修改原来的字符串。
实际上,要找到动态规划中的递推式并不容易,如果是机试遇到的题目没有见过,那还是大概率凉凉,还是要多积累。
另一个细节是这里的dp数组记录的是差值,即我们需要的值,我想这个是关键点,无论是不是动态规划,这个翻转的本质就是某子段中的数量差值,这是很自然能想到的。
#include<iostream>
#include<string>
using namespace std;
int main(){
int n;
while(cin>>n){
string s;
cin>>s;
int sum = 0,sum1=0,dp[n];
if(s[0]=='0'){dp[0]=1;}
else {dp[0] = 0;sum1++;}//这里是对于递推式的首项
for(int i=1;i<n;i++){
if(s[i]=='0') { dp[i] =dp[i-1]++;}
else {sum1++;dp[i] = max(dp[i-1]--,0);} //这里的sum1是统计1的数量
sum = max(sum,dp[i]);} //这里的sum是0和1之间的数量差值,并不是0的数量
cout<<sum1+sum<<endl;
}
return 0;
}
1334
与之前题目思路一致,但是需要注意一些细节
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e4+5;
int a[maxn],b[maxn];
long long maxx;
int main()
{
int n;
while(cin>>n)
{
if(n==0)break;
for(int i=0;i<n;i++)
cin>>a[i];
int maxx=a[0],beg=0,endd=0,temp=0;
b[0]=a[0];
for(int i=1;i<n;i++)
{
if(b[i-1]+a[i]>a[i])
{
b[i]=b[i-1]+a[i];
}
else
{
b[i]=a[i];
temp=i;
}
if(maxx<b[i])
{
beg=temp;
endd=i;
maxx=b[i];
}
}
if(maxx<0)
cout<<0<<" "<<a[0]<<" "<<a[n-1]<<endl;
else
cout<<maxx<<" "<<a[beg]<<" "<<a[endd]<<endl;
}
return 0;
}
最长上升子序列
子序列和子段的差别,子序列不要求连续,子段要求连续
1257
核心
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e4+5;
int a[maxn],b[maxn],n;
int lis_nn()
{
int ans=0;
for(int i=0;i<n;i++)
{
b[i]=a[i];
for(int j=0;j<i;j++)
{
if(a[j]<a[i])
b[i]=max(a[i]+b[j],b[i]);
if(b[i]>ans)
ans=b[i];
}
}
cout<<ans<<endl;
return 0;
}
int main()
{
while(cin>>n)
{
for(int i=0;i<n;i++)
cin>>a[i];
lis_nn();
}
return 0;
}
1256
差不多,打卡题
#include <bits/stdc++.h>
using namespace std;
const int maxn=100+5;
int a[maxn],b[maxn];
int main()
{
int n;
while(cin>>n)
{
int ans=0;
for(int i=n;i>0;i--)
cin>>a[i];
for(int i=1;i<=n;i++)
{
b[i]=1;
for(int j=1;j<i;j++)
{
if(a[j]<=a[i])
b[i]=max(b[j]+1,b[i]);
if(b[i]>ans) ans=b[i];
}
}
cout<<ans<<endl;
}
return 0;
}
1253
这道题走了大弯路
原算法
#include <bits/stdc++.h>
using namespace std;
const int maxn=100+5;
int a[maxn],b[maxn],n;
int sheng(int k)
{
int ans=0;
for(int i=1;i<=k;i++)
{
b[i]=1;
for(int j=1;j<i;j++)
{
if(a[j]<a[i])
b[i]=max(b[j]+1,b[i]);
}
if(b[i]>ans) ans=b[i];
}
return k-ans;
}
int jiang(int k)
{
int ans=0;
for(int i=k;i<=n;i++)
{
b[i]=1;
for(int j=k;j<i;j++)
{
if(a[j]>a[i])
b[i]=max(b[j]+1,b[i]);
}
if(b[i]>ans) ans=b[i];
}
return n-k-ans+1;
}
int main()
{
while(cin>>n)
{
int minx=1e5;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int k=1;k<=n;k++)
{
int ans1=sheng(k);
int ans2=jiang(k);
//cout<<"k "<<k<<" ans1 "<<ans1<<" ans2 "<<ans2<<endl;
minx=min(minx,ans1+ans2);
}
cout<<minx<<endl;
}
return 0;
}
实际上每次b[i]已经是该序列的最长了
改进
#include<bits/stdc++.h>
using namespace std;
int a[105];
int dp[105];
int dp2[105];
int n;
int LIS_nn(){
int ans = 0;
for(int i = 0;i<n;i++){
dp[i] = 1;
for(int j = 0;j<i;j++){
if(a[j]<a[i])
dp[i] = max(dp[i],dp[j]+1);
}
//ans = max(ans,dp[i]);
}
for(int i = n-1;i>=0;i--){
dp2[i] = 1;
for(int j = n-1;j>i;j--){
if(a[j]<a[i])
dp2[i] = max(dp2[i],dp2[j]+1);
}
ans = max(ans,dp[i]+dp2[i]-1);
}
return ans;
}
int main(){
while(cin>>n){
for(int i = 0;i<n;i++){
cin>>a[i];
}
cout<<n-LIS_nn()<<endl;
}
return 0;
}
最长公共子序列
序列 不指连续
核心
1293
#include<iostream>
#include<string>
#include<string.h>
#include<vector>
#include<stdio.h>
#include <queue>
#include <functional>
#include<algorithm>
using namespace std;
const int maxn=100+5;
int a[maxn][maxn];
string s1,s2;
int main()
{
memset(a,0,sizeof(a));
while(cin>>s1>>s2)
{
int len1=s1.size();
int len2=s2.size();
for(int i=1;i<=len1;i++)
{
for(int j=1;j<=len2;j++)
{
if(s1[i-1]==s2[j-1])
a[i][j]=a[i-1][j-1]+1;
else
a[i][j]=max(a[i-1][j],a[i][j-1]);
}
}
cout<<a[len1][len2]<<endl;
}
return 0;
}
背包问题
很难
1035
#include <bits/stdc++.h>
using namespace std;
const int maxn=1000+5;
int a[maxn][maxn];
int w[maxn];
int main()
{
memset(a,0,sizeof(a));
int sum,n;
while(cin>>sum>>n)
{
for(int i=1;i<=n;i++)
cin>>w[i];
a[0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=0;j<=sum;j++)
{
if(j>=w[i]&&a[i-1][j-w[i]]==1)//放
a[i][j]=1;
if(a[i-1][j]==1)//不放
a[i][j]=1;
}
}
if(a[n][sum]==1)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
return 0;
}
1123
同上一题
1086
同上一题
1567
难
求路径也是用前面路径推算后面路径
传送门
记忆化搜索
1568
和前面的搜索差不多,加了记忆化,就是每次到达这个点,直接用这个点的之前的结果,而不再遍历
#include<iostream>
#include<string>
#include<string.h>
#include<vector>
#include<stdio.h>
#include <queue>
#include <functional>
#include<algorithm>
using namespace std;
const int maxn=100+5;
int mpt[maxn][maxn];
int r,c;
int flag[maxn][maxn];
int fang[4][2]={0,1,0,-1,1,0,-1,0};
int dfs(int x,int y)
{
if(flag[x][y])//已经走过
return flag[x][y];
int nx,ny,maxx=1;
for(int i=0;i<4;i++)
{
nx=x+fang[i][0];
ny=y+fang[i][1];
if(nx>=1&&nx<=r && ny>=1&&ny<=c && mpt[nx][ny]<mpt[x][y])
{
maxx=max(maxx,dfs(nx,ny)+1);
}
}
flag[x][y]=maxx;
return maxx;
}
int main()
{
while(cin>>r>>c)
{
memset(flag,0,sizeof(flag));
memset(mpt,0,sizeof(mpt));
for(int i=1;i<=r;i++)
for(int j=1;j<=c;j++)
cin>>mpt[i][j];
int ans=0;
for(int i=1;i<=r;i++)
{
for(int j=1;j<=c;j++)
{
flag[i][j]=dfs(i,j);
ans=max(ans,flag[i][j]);
}
}
cout<<ans<<endl;
}
return 0;
}