文章目录
动态规划1及练习题
前导知识
:
相关知识请参考OI Wiki之动态规划板块
1. 编辑距离
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
string a,b;
char s1[2005],s2[2005];
int mem[2005][2005];
int dp(int i,int j){
if(mem[i][j]!=-1) return mem[i][j];
if(i==0) return mem[i][j]=j;
if(j==0) return mem[i][j]=i;
int w=1;
if(s1[i]==s2[j]) w=0;
return mem[i][j]=min(min(dp(i-1,j)+1,dp(i,j-1)+1),dp(i-1,j-1)+w);
}
int main(){
cin>>a>>b;
memset(mem,-1,sizeof(mem));
int len1=a.length(),len2=b.length();
for(int i=1;i<=len1;i++){
s1[i]=a[i-1];
}
for(int i=1;i<=len2;i++){
s2[i]=b[i-1];
}
dp(len1,len2);
cout<<mem[len1][len2];
return 0;
}
解题思路
:
假设 f [ i ] [ j ] f[i][j] f[i][j]表示将 a [ 1... i ] a[1...i] a[1...i]转换为串 b [ 1... j ] b[1...j] b[1...j]所需的最少操作次数,首先是边界:
- i = = 0 i==0 i==0时,即 a a a为空,那么对应的 f [ 0 ] [ j ] f[0][j] f[0][j]的值就为 j j j:增加 j j j个字符,使 a a a转化为 b b b。
- j = = 0 j==0 j==0时,即 b b b为空,那么对应的 f [ i ] [ 0 ] f[i][0] f[i][0]的值就为 i i i:减少 i i i个字符,使 a a a转化为 b b b。
然后考虑一般情况(这里DP思想):我们要得到将 a [ 1... i ] a[1...i] a[1...i]经过最少次数的操作就转化为 b [ 1... j ] b[1...j] b[1...j],那么我们就必须在此之前以最少次数(假设为 k k k次)的操作,使现在的 a a a和 b b b只需再做一次操作或者不做操作就可以使 a [ 1... i ] a[1...i] a[1...i]转化到 b [ 1... j ] b[1...j] b[1...j]。而之前有三种情况:
- 将 a [ 1... i ] a[1...i] a[1...i]转化为 b [ 1... j − 1 ] b[1...j-1] b[1...j−1]
- 将 a [ 1... i − 1 ] a[1...i-1] a[1...i−1]转化为 b [ 1... j ] b[1...j] b[1...j]
- 将 a [ 1... i − 1 ] a[1...i-1] a[1...i−1]转化为 b [ 1... j − 1 ] b[1...j-1] b[1...j−1]
第1种情况,只需要在最后将 b [ j ] b[j] b[j]加在 a [ 1... i ] a[1...i] a[1...i]的后面,总共就需要 k + 1 k+1 k+1次操作
第2种情况,只需要在最后将 a [ i ] a[i] a[i]删除,总共需要 k + 1 k+1 k+1次操作
第3种情况,只需要在最后将 a [ i ] a[i] a[i]替换为 b [ j ] b[j] b[j],总共需要 k + 1 k+1 k+1次操作。但如果 a [ i ] a[i] a[i]刚好等于 b [ j ] b[j] b[j],就不用再替换了,就只需要 k k k个操作。
上述三种情况取最小值即可
2. 最短路计数
3. [NOIP 2002 普及组] 过河卒
#include <iostream>
#include <cstring>
using namespace std;
#define MAX_N 110
bool b[MAX_N][MAX_N];
long long a[MAX_N][MAX_N];
const int dx[8]={2,1,-1,-2,-2,-1,1,2};
const int dy[8]={1,2,2,1,-1,-2,-2,-1};
int n,m,x,y;
int main(){
cin>>n>>m>>x>>y;
memset(b,0,sizeof(b));
b[x][y]=1;
for(int i=0;i<=7;i++){
if(x+dx[i]>=0&&x+dx[i]<=n&&y+dy[i]>=0&&y+dy[i]<=m){
b[x+dx[i]][y+dy[i]]=1;
}
}
for(int i=0;!b[i][0]&&i<=n;){
a[i++][0]=1;
}
for(int i=0;!b[0][i]&&i<=m;){
a[0][i++]=1;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(b[i][j]){
a[i][j]=0;
}else{
a[i][j]=a[i-1][j]+a[i][j-1];
}
}
}
cout<<a[n][m];
return 0;
}
原理
:
根据加法原理
,到达某一点的路径数目,就等于到达其相邻的上点和左点的路径数目之和,因此我们可以使用逐列(或逐行)递推的方法来求出从起点到终点的路径数目,障碍点(马的控制点)也完全适用,只要将到达该点的路径数目设置为0即可。
递推关系
:
a
[
i
]
[
j
]
=
a
[
i
−
1
]
[
j
]
+
a
[
i
]
[
j
−
1
]
a[i][j]=a[i-1][j]+a[i][j-1]
a[i][j]=a[i−1][j]+a[i][j−1]
4. [SHOI2002]滑雪
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int dx[4]={0,0,1,-1};
const int dy[4]={1,-1,0,0};
int n,m,a[201][201],mem[201][201],ans;
int dp(int x,int y){
if(mem[x][y]) return mem[x][y];
mem[x][y]=1;
for(int i=0;i<4;i++){
int xx=x+dx[i];
int yy=y+dy[i];
if(xx>0&&yy>0&&xx<=n&&yy<=m&&a[x][y]>a[xx][yy]){
dp(xx,yy);
mem[x][y]=max(mem[x][y],mem[xx][yy]+1);
}
}
return mem[x][y];
}
int main(){
cin>>n>>m;
memset(mem,0,sizeof(mem));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>a[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
ans=max(ans,dp(i,j));
cout<<ans;
return 0;
}
5. [NOIP2004 提高组] 合唱队形
#include <cstdio>
#include <algorithm>
using namespace std;
int n,a[105],f[2][105],ans;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
a[0]=0;
for(int i=1;i<=n;i++)
for(int j=0;j<i;j++)
if(a[i]>a[j]) f[0][i]=max(f[0][i],f[0][j]+1);
a[n+1]=0;
for(int i=n;i>=1;i--)
for(int j=n+1;j>i;j--)
if(a[i]>a[j]) f[1][i]=max(f[1][i],f[1][j]+1);
for(int i=1;i<=n;i++)
ans=max(f[0][i]+f[1][i]-1,ans);
printf("%d\n",n-ans);
return 0;
}
6. [USACO1.5][IOI1994]数字三角形 Number Triangles
#include <cstdio>
int n,a[1002],ans,p;
int _max(int &x,int &y){
return x>y?x:y;
}
int main(){
scanf("%d",&n);
for(int i=n;i>=1;i--)
for(int j=i;j<=n;j++)
scanf("%d",&p),a[j]=_max(a[j],a[j+1])+p;
for(int i=1;i<=n;i++)
ans=_max(ans,a[i]);
printf("%d",ans);
return 0;
}
每走一步,对其中的路径求和取每次的最大值