动态规划1

动态规划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]所需的最少操作次数,首先是边界:

  1. 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
  2. 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]​。而之前有三种情况:

  1. a [ 1... i ] a[1...i] a[1...i]转化为 b [ 1... j − 1 ] b[1...j-1] b[1...j1]
  2. a [ 1... i − 1 ] a[1...i-1] a[1...i1]转化为 b [ 1... j ] b[1...j] b[1...j]
  3. a [ 1... i − 1 ] a[1...i-1] a[1...i1]转化为 b [ 1... j − 1 ] b[1...j-1] b[1...j1]

第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[i1][j]+a[i][j1]

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;
}

每走一步,对其中的路径求和取每次的最大值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DeeGLMath

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值