\(DP-20\)
最近要写计划,当然,就是讲自己的弱项之类的写上去,脑子一抽写了个九月份之前\(20\)道\(DP\)题,现在就只能橙黄相伴了.....现在是把做过的DP的题写一下题解报告吧......
1.又上锁妖塔(Link)
妖塔的建造很特别,塔总共有\(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;
}
2.编辑距离(Link)
设\(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;
}