$\color{red}{DP - 20}$~~(水)~~(持续更新中)

\(DP-20\)

最近要写计划,当然,就是讲自己的弱项之类的写上去,脑子一抽写了个九月份之前\(20\)\(DP\)题,现在就只能橙黄相伴了.....现在是把做过的DP的题写一下题解报告吧......

妖塔的建造很特别,塔总共有\(n\)层,但是高度却不相同,这造成了小\(A\)爬过每层的时间也不同.小\(A\)会用仙术,每用一次可以让他向上跳一层或两层,但是每次跳跃后小\(A\)都将用完灵力,必须爬过至少一层才能再次跳跃(你可以认为小\(A\)需要跳两次一层才休息),小\(A\)想用最短的时间爬到塔顶,可是他不能找到时间最短的方案,所以请你帮他找到一个时间最短的方案让他爬到塔顶,小\(A\)只关心时间,所以你只要告诉他最短时间是多少就可以了.你可以最后跳到塔外即超过塔高.

输入输出格式

输入格式:

第一行一个数\(n (n<=1000000)\),表示塔的层数.

第二行\(n\)个数\((<=100)\),表示从下往上每层的高度.

输出格式:

一个数,表示最短时间.

首先,按照DP的常规思路,我们先找到子问题:

\(f[i]\)为爬上第\(i\)层的最短时间。那么我们可以很容易地确定出子问题:

1.是从\(i-1\)层爬上第\(i\)层的
2.是用魔法飞上第\(i\)层的

我们知道爬上第\(i\)层肯定是要从第\(i-1\)层,即\(\color{red}{f[i]=f[i-1]+high[i]}\).

然后至于第二种情况,我们又要分两种情况:1.从\(i-2\)层跳上来,2.从\(i-3\)层跳上来,时间要取最小值,那么就是\(\color{red}{f[i]=min(f[i-2]+high[i],f[i-3]+high[i])}\)

下面是代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 10010
using namespace std;
int n,dp[MAXN];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    scanf("%d",&dp[i]); 
    for(int i=1;i<=n+1;i++)
    dp[i]=min(dp[i-1],min(dp[i-2],dp[i-3]))+dp[i];
    //对于所有的情况,直接取min就好了。
    printf("%d",dp[n+1]);
    return 0;
}

\(A\)\(B\)是两个字符串。我们要用最少的字符操作次数,将字符串\(A\)转换为字符串\(B\)。这里所说的字符操作共有三种:

1、删除一个字符;
2、插入一个字符;
3、将一个字符改为另一个字符;
(!皆为小写字母!)

输入输出格式

输入格式:

第一行为字符串\(A\);第二行为字符串\(B\);字符串\(A\)\(B\)的长度均小于\(2000\)

输出格式:

只有一个正整数,为最少字符操作次数。

子问题确定:对于一个状态\(i\)我们只有四种操作方式:删除、插入、改变、不变。所以状态\(i\)就是由前一个状态进行这四步操作转化过来的,所以我们的自问题也就是\(4\)个。

首先我们设\(dp[i][j]\)为字符串\(A\)的前\(i\)个字符变为字符串\(B\)的前\(j\)个字符需要多少步,然后最后的答案显然就是\(dp[lenA][lenB]\);

那么再根据子问题分开来谈状态转移方程。

\(1.\)删除。我们直接选择删除最后一个,那么也就变成了\(A\)的前\(i-1\)个字符变为\(B\)的前\(j\)个字符需要多少步,删除需要步数\(+1\)。即\(\color{red}{dp[i][j]=dp[i-1][j]+1}\);

\(2.\)添加。我们选择在\(A\)的最后面添加一个字符,那么肯定是为了使最后一个字符相匹配,那么最后一个字符也就匹配上可以删去,那么我们可以理解为\(A+1-1\)\(B-1\),那么就是\(j-1\)咯。同样,添加也需要步数\(+1\)\(\color{red}{dp[i][j]=dp[i][j-1]+1}\);

\(3.\)替换,我们替换\(A\)字符串的最后一个字符为\(B\)的最后一个字符,那么\(A\)\(B\)的最后一位显然必定也是匹配上可以删除了,那么\(i\)\(j\)都要减一,同样步数要\(+1\),即:\(\color{red}{dp[i][j]=dp[i-1][j-1]+1}\);

\(4.\)不变。字符串\(A\)\(B\)的最后一个字符都相等,那么自然不要变咯,同样删除最后一个字符,但是步数并不用\(+1\),因为我们并没有进行操作,那么方程就是\(\color{red}{dp[i][j]=dp[i-1][j-1]}\);

然后最后的状态转移方程就是:

\(\color{red}{dp[i][j]=min(min(dp[i-1][j]+1,dp[i][j-1]+1),dp[i-1][j-1)+b}\)

当然,这个\(b\)是要看\(A\)\(B\)的最后一个字符是不是相等。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 2010
using namespace std;
string a,b;
char s1[MAXN],s2[MAXN];
int f[MAXN][MAXN];
int DP(int i,int j){
    if(f[i][j]!=-1) return f[i][j];
    if(i==0) return f[i][j]=j;
    if(j==0) return f[i][j]=i;
    int ken=1;
    if(s1[i]==s2[j]) ken=0;
    return f[i][j]=min(min(DP(i-1,j)+1,DP(i,j-1)+1),DP(i-1,j-1)+ken);
}
int main(){
    cin>>a>>b;
    int len1=a.length();
    int len2=b.length();
    memset(f,-1,sizeof(f));
    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);
    printf("%d",f[len1][len2]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Yeasio-Nein/p/DP-20.html