【线性DP】基础练习

1、数字金字塔

考虑在下面被显示的数字金字塔。

写一个程序来计算从最高点开始在底部任意处结束的路径经过数字的和的最大。
每一步可以走到左下方的点也可以到达右下方的点。
7

3 8

8 1 0

2 7 4 4

4 5 2 6 5
在上面的样例中,从7 到 3 到 8 到 7 到 5 的路径产生了最大和:30

PROGRAMNAME: numtri

INPUTFORMAT
第一个行包含 R(1<= R<=1000) ,表示行的数目。
后面每行为这个数字金字塔特定行包含的整数。
所有的被供应的整数是非负的且不大于100。

SAMPLEINPUT ( numtri.in)
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

OUTPUTFORMAT
单独的一行包含那个可能得到的最大的和。

SAMPLEOUTPUT ( numtri.out)
30

【思路】这里用自底向上的做法,最后找答案比较方便

#include<cstdio>

int f[1001][1001],a[1001][1001];
int i,j,n;
int main()
{
    freopen("numtri.in","r",stdin);
    freopen("numtri.out","w",stdout);
    scanf("%d",&n);
    for (i=1;i<=n;i++)
       for (j=1;j<=i;j++) scanf("%d",&a[i][j]);
    for (i=1;i<=n;i++) f[n][i]=a[n][i];
    for (i=n-1;i>0;i--)
    {
       for (j=1;j<=n;j++)
         if (f[i+1][j+1]>f[i+1][j]) f[i][j]=a[i][j]+f[i+1][j+1];
         else f[i][j]=a[i][j]+f[i+1][j];     
    }
    printf("%d\n",f[1][1]);
}         

 2、逢低吸纳

“逢低吸纳”是炒股的一条成功秘诀。如果你想成为一个成功的投资者,就要遵守这条秘诀: 

"逢低吸纳,越低越买" 

这句话的意思是:每次你购买股票时的股价一定要比你上次购买时的股价低.按照这个规则购买股票的次数越多越好,看看你最多能按这个规则买几次。

给定连续的N天中每天的股价。你可以在任何一天购买一次股票,但是购买时的股价一定要比你上次购买时的股价低。写一个程序,求出最多能买几次股票。 

以下面这个表为例, 某几天的股价是: 

天数 1 2 3 4 5 6 7 8 9 10 1112
股价 68 69 54 64 68 64 70 6778 62 98 87

这个例子中, 聪明的投资者(按上面的定义),如果每次买股票时的股价都比上一次买时低,那么他最多能买4次股票。一种买法如下(可能有其他的买法): 

天数 2 5 6 10
股价 69 68 64 62

PROGRAM NAME:buylow

INPUT FORMAT

第1行: 

N (1 <= N <= 5000), 表示能买股票的天数。

第2行以下:

N个正整数 (可能分多行) ,第i个正整数表示第i天的股价. 这些正整数大小不会超过longint(pascal)/long(c++).

SAMPLE INPUT(buylow.in)

12
68 69 54 64 68 64 70 67
78 62 98 87

OUTPUT FORMAT

只有一行,输出一个整数:

  • 能够买进股票的天数

SAMPLEOUTPUT (buylow.out)

4

 

【思路】最长不下降子序列

#include<cstdio>

int n,i,j,max;
long v[5000];
int f[5000];

void Max(int a,int &b) { if (a>b) b=a; }
      
int main()
{
   freopen("buylow.in","r",stdin);
   freopen("buylow.out","w",stdout);
   scanf("%d",&n);
   for (i=0;i<n;i++)
    {
       scanf("%ld",&v[i]);
       f[i]=1;
       }
   for (i=0;i<n;i++)
     for (j=0;j<i;j++)
     {
         if (v[i]<v[j]) Max(f[j]+1,f[i]);   
         Max(f[i],max);
         }
   printf("%d",max);
}
                      

(3)4、挖地雷(mine.pas)
   在一个地图上有N个地窖(N<=20),每个地窖中埋有一定数量的地雷。同时,给出地窖之间的连接路径,并规定路径都是单向的。某人可以从任一处开始挖地雷,然后沿着指出的连接往下挖(仅能选择一条路径),当无连接时挖地雷工作结束。设计一个挖地雷方案,使他能挖到最多的地雷。

   输入格式:(mine.in)

N:

W1,w2,w3,……wn

   A12……A1N

     A23……A2N

     ……

     AN-1N

 输出格式:(mine.out)

   K1----K2--……KV (挖地雷的顺序)

   MAX            (挖地雷的数量)

例:

输入:6

510 20 5 4 5

10 1 0 0

01 0 0

10 0

11

1

输出:3-4-5-6

34


 【思路】  f[i]表示第I个地雷处所能挖的最大值

                  pre[i]记录I的前驱 用于输出路径

   

#include<cstdio>

int n,i,j,max;
int w[21],f[21],pre[21],a[21][21];


void road(int x)
{
   if (!pre[x]) { printf("%d-",x); return; }
   road(pre[x]);
   printf("%d-",x); 
}     



int main()
{
   freopen("mine.in","r",stdin);
   freopen("mine.out","w",stdout); 
   scanf("%d",&n);
   for (i=1; i<=n; i++) scanf("%d",&w[i]);
   for (i=1; i<=n; i++) 
   {
     for (j=i+1; j<=n; j++) scanf("%d",&a[i][j]);
     f[i]=w[i]; pre[i]=0;
     } 
  /* for (i=2; i<=n; i++)
     for (j=1; j<i; j++)
        if (a[j][i] && f[j]+w[i]>f[i])
             {
                f[i]=f[j]+w[i];
                pre[i]=j;
                if (f[i]>f[max]){ max=i; } 
             }  */  //这是第一次写错的, 比较答案的时候放错地方了【注意】
   for (i=2; i<=n; i++)
    {
     for (j=1; j<i; j++)
        if (a[j][i] && f[j]+w[i]>f[i])
             {
                f[i]=f[j]+w[i];
                pre[i]=j;          
             } 
       if (f[i]>f[max]) max=i;        
    }         
    if (pre[max]) road(pre[max]);
    printf("%d\n",max);
    printf("%d\n",f[max]);
}                                           

 

5、 拦截导弹(intercept.pas)

  某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
  输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000 的正整数),计算这套系统最多能拦截多少导弹,和如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

输入:n颗依次飞来的高度(1≤n≤1000)。

输出:一套系统最多拦截的导弹数best,并依次列出被拦截导弹的序号,要拦截所有导弹最少配备的系统数k。

样例:
INPUT (intercept.in)
389 207 155 300 299 170 158 65 
output(intercept.out)
  6(最多能拦截的导弹数)                  
  2(要拦截所有导弹最少要配备的系统数)

【思路】第一问就是求最长不上升子序列

               第二问:若后一个导弹比当前导弹高,则必不能被当前导弹打下,其实就是求最长上升子序列

               可以合在一起写比较快 这里分开比较清楚

#include<cstdio>
#include<cstring>

int n=0,i,j;
int f[1000],a[1000];


void solve1()
{
   int max=1;
   for (i=0;i<n;i++)
   {
     f[i]=1;  
     for (j=0;j<i;j++)
         if (a[i]<=a[j] && f[j]+1>f[i]) f[i]=f[j]+1;
     if (f[i]>max) max=f[i];
   }
   printf("%d\n",max);
}

void solve2()
{
    int max=1;
    for (i=0;i<n;i++)
    {
       f[i]=1; 
       for (j=0;j<i;j++)
         if (a[i]>a[j] && f[j]+1>f[i]) f[i]=f[j]+1;
      if (f[i]>max) max=f[i];
    }
    printf("%d\n",max);
}                     

int main()
{
   freopen("intercept.in","r",stdin);
   freopen("intercept.out","w",stdout); 
   while (scanf("%d",&a[n])!=EOF) {n++;};
   solve1();
   solve2(); 
} 
   

6、 合唱队形(chorus.pas/dpr/c/cpp)

【问题描述】

N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。

合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2, …, K,他们的身高分别为T1,T2, …, TK,则他们的身高满足T1 < T2 < … < Ti , Ti> Ti+1 > … > TK (1 <= i <= K)。

你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

【输入文件】

输入文件chorus.in的第一行是一个整数N(2 <= N <= 100),表示同学的总数。第一行有n个整数,用空格分隔,第i个整数Ti(130 <= Ti<= 230)是第i位同学的身高(厘米)。

【输出文件】

输出文件chorus.out包括一行,这一行只包含一个整数,就是最少需要几位同学出列。

【样例输入】

8

186 186 150 200 160 130 197 220

【样例输出】

4

【数据规模】

对于50%的数据,保证有n <= 20;

对于全部的数据,保证有n <= 100。


【思路】先从左到右做一遍最长上升子序列,再倒着做一遍,然后枚举中间人,找出最大的l[i]+r[i]-1 就是能排成的最长队伍,最后答案=n-max

#include<cstdio>

int i,n,j,ans,l[101],r[101],t[101];

void left()
{
   for (i=1; i<=n; i++)
    {
       l[i]=1;      
       for (j=1;j<i;j++)
         if (t[j]<t[i]  &&  l[j]+1>l[i]) l[i]=l[j]+1;
    }
}           

void right()
{
    for (i=n;i>0;i--)
    {
      r[i]=1;  
      for (j=n;j>i;j--) 
          if (t[j]<t[i] &&  r[j]+1>r[i]) r[i]=r[j]+1;
      }
}           

void solve()
{
   ans=0;  
   for (i=1; i<=n;i++)
      if (l[i]+r[i]-1>ans) ans=l[i]+r[i]-1;
   ans=n-ans;
}

int main( )
{
   freopen("chorus.in","r",stdin);
   freopen("chorus.out","w",stdout); 
   scanf("%d",&n);
   for (i=1;i<=n;i++) scanf("%d",&t[i]);
   left();
   right();
   solve();
   printf("%d\n",ans);
}  

7、轮船问题(ship.pas)

〖题目描述〗

某国家被一条河划分为南北两部分,在南岸和北岸总共有N 对城市,每一城市在对岸都有唯一的友好城市,任何两个城市都没有相同的友好城市。每一对友好城市都希望有一条航线来往,于是他们向政府提出了申请。由于河终年有雾。政府决定允许开通的航线就互不交叉(如果两条航线交叉,将有很大机会撞船)。

你的任务是编写一个程序来帮政府官员决定他们应拨款兴建哪些航线以使在安全条件下有最多航线可以被开通。

【输入格式】

输入文件(ship.in):包括了若干组数据,每组数据格式如下:

第一行两个由空格分隔的整数x,y,10〈=x〈=6000,10〈=y〈=100。x 表示河的长度而y 表示宽。第二行是一个整数N(1<=N<=5000),表示分布在河两岸的城市对数。接下来的N 行每行有两个由空格分隔的正数C,D(C、D〈=x〉),描述每一对友好城市沿着河岸与西边境线的距离,C 表示北岸城市的距离而D 表示南岸城市的距离。在河的同一边,任何两个城市的位置都是不同的。整个输入文件以由空格分隔的两个0 结束。

【输出格式】

输出文件(ship.out):要在连续的若干行里给出每一组数据在安全条件下能够开通的最大航线数目。

【输入输出样例】

Ship.in

30 4

7

22 4

2 6

10 3

15 12

9 8

17 17

4 2

0 0

 

Ship.out

4


【思路】将航线按任意一岸城市位置升序排序,若航线a,b交叉,则另一岸的citya>cityb,故求另一岸的最长不下降子序列长度就是答案

#include <cstdio>
#include <algorithm>
using namespace std;

int x,y,n,i,j;
int f[5000];
struct City{
       int c,d;
       }city[5000];

int cmp(const City &a,const City &b){
    if (a.c<b.c)  return 1;
      else return 0;
      }
       

int main()
{
   freopen("ship.in","r",stdin);
   freopen("ship.out","w",stdout); 
   while (scanf("%d%d",&x,&y) && x && y){
       scanf("%d",&n);
       for (i=0; i<n; i++) scanf("%d%d",&city[i].c,&city[i].d);
       sort(city,city+n,cmp);
       int max=1;
       for (i=0;i<n;i++){
           f[i]=1;
           for (j=0;j<i;j++)
             if (city[j].d<city[i].d && f[j]+1>f[i]) f[i]=f[j]+1;
           if (f[i]>max) max=f[i];
           }
       printf("%d\n",max);
       }
}           
                           


8、逢低吸纳2

“逢低吸纳”是炒股的一条成功秘诀。如果你想成为一个成功的投资者,就要遵守这条秘诀: 

"逢低吸纳,越低越买" 

这句话的意思是:每次你购买股票时的股价一定要比你上次购买时的股价低.按照这个规则购买股票的次数越多越好,看看你最多能按这个规则买几次。
给定连续的N天中每天的股价。你可以在任何一天购买一次股票,但是购买时的股价一定要比你上次购买时的股价低。写一个程序,求出最多能买几次股票。 
以下面这个表为例,某几天的股价是: 
天数1 2 3 4 5 6 7 8 9 10 11 12
股价68 69 54 64 68 64 70 67 78 62 98 87

这个例子中,聪明的投资者(按上面的定义),如果每次买股票时的股价都比上一次买时低,那么他最多能买4次股票。一种买法如下(可能有其他的买法): 

天数2 5 6 10
股价69 68 64 62

PROGRAM NAME: buylow1

INPUT FORMAT

第1行: 

N (1 <= N <= 5000), 表示能买股票的天数。

第2行以下:

N个正整数 (可能分多行) ,第i个正整数表示第i天的股价. 这些正整数大小不会超过longint(pascal)/long(c++).

SAMPLE INPUT ( buylow1.in)

12
68 69 54 64 68 64 70 67
78 62 98 87

OUTPUT FORMAT

只有一行,输出两个整数:

  • 最大购买次数和拥有最大购买次数的方案数(小于等于2^31)当二种方案“看起来一样”时(就是说它们构成的价格队列一样的时候),这2种方案被认为是相同的。

SAMPLEOUTPUT (buylow1.out)

4 2

【思路】主题去重还是不会,没搞懂,附上hhy大神题解

Program p8_3(Input, Output);
const maxn=5000;
var i,j,n,l,maxlen:longint;
    a,len,t:array [0..maxn] of longint;

begin
 // assign(input,'buylow1.in');  reset(input);
 // assign(output,'buylow1.out'); rewrite(output);
 readln(n);
  for i:=1 to n do read(a[i]);
  for i:=1 to n do len[i]:=1;
  for i:=n-1 downto 1 do
      for j:=i+1 to n do
          if (a[j]<a[i]) and (len[j]>=len[i]) then len[i]:=len[j]+1;
  maxlen:=1;
  for i:=1 to n do if len[i]>maxlen then maxlen:=len[i];
  a[0]:=maxlongint; len[0]:=maxlen+1;
  for i:=0 to n do if len[i]=1 then t[i]:=1 else t[i]:=0;
  for l:=1 to maxlen do
  begin
       for i:=n downto 1 do
           if len[i]=l then
              begin
                   j:=i-1;
                   while (j>=0) and (a[j]<>a[i]) do
                   begin
                        if (a[j]>a[i]) and (len[j]=l+1) then inc(t[j],t[i]);
                        dec(j)
                   end;
              end;
  end;
  writeln(maxlen,' ',t[0]);
  close(input);
  close(output);
end.
{ a:4 3 3 1
len:3 2 2 1
  t:1 1 1 1
while (j>=0) and (a[j]<>a[i]) do 时当算到len=2时,倒数的那个3遇到倒数第二个3就退出循环就不计数了,原理:后面那个3与前一个3大小相同,则长度要么相同,要么大,所以长度相同的话,数值也是一样的,所以前面的就不累加,只要计最后一个即可}


 
 

9、最长上升子序列

文件名:zcx.pas

[问题描述]

设有整数序列b1,b2,b3,…,bm,若存在下标i1<i2<i3< …<in,且bi1<bi2<bi3< …<bin,则称 b1,b2,b3,…,bm中有长度为n的上升序列bi1 , bi2 ,bi3 ,…,bin 。

求序列b1,b2,b3,…,bm中所有长度为(n)最长上升子序列

[输入]

第1行一个正整数n

第2行到第n+1行为n个正整数ai

[输出]

一个整数ans,即最长上升子序列长度

[数据规模] 

对于30%的数据 n<=10

对于60%的数据 n<=1000

对于100%的数据n<=1000000

 

[样例]

zcx.in

5

1

4

2

3

7

zcx.out

4

[注释]

样例中最长上升序列为1237

【思路】最长上升子序列的Nlogn优化做法

/*最长上升子序列NlogN*/ 
#include<cstdio>
long n,i,top,x,p;
long m[1000000];

long search(long x,long l,long r)
{
   long mid,left,right;
   left=l; right=r-1; mid=(left+right)>>1;
   while (l<=r && !(m[mid]<=x && m[mid+1]>=x)) //注意二分条件 
   {
     if (x>m[mid]) left=mid+1;
     if (x<m[mid]) right=mid-1;
     mid=(left+right)>>1;
     }
   return mid;
}  
     
          

int main()
{
    freopen("zcx.in","r",stdin);
    freopen("zcx.out","w",stdout);
    scanf("%ld",&n);
    scanf("%ld",&x);
    top=1;
    m[top]=x;
    for (i=1;i<n;i++)
    {
        scanf("%ld",&x);
        if (x>m[top]) m[++top]=x;
        else 
        {
           p=search(x,1,top); //1~top-1
           m[p+1]=x;
           }
    }
    printf("%ld\n",top);   
   // getchar();
   // getchar();   
}


10、最长公共子序列问题(LCS)

一个给定序列的子序列是在该序列中删去若干元素后得到的序列。确切地说,若给定序列X=<x1,x2,…, xm>,则另一序列Z=<z1,z2,…, zk>X的子序列是指存在一个严格递增的下标序列 <i1,i2,…, ik>,使得对于所有j=1,2,…,k:Zj=Xij.

例如,序列Z=<B,C,D,B>是序列X=<A,B,C,B,D,A,B>的子序列,相应的递增下标序列为<2,3,5,7>

给定两个序列XY,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列XY的公共子序列。例如,若X=<A, B, C, B, D, A,B>Y=<B, D, C, A, B, A>,则序列<B, C, A>XY的一个公共子序列,序列<B, C, B, A>也是XY的一个公共子序列。而且,后者是XY的一个最长公共子序列,因为XY没有长度大于4的公共子序列。

最长公共子序列(LCS)问题:给定两个序列X=<x1,x2, …, xm>Y=<y1,y2, … , yn>,要求找出XY的一个最长公共子序列。

样例:

LCS.IN

ABCBDAB

BDCABA

LCS.OUT

4

BCBA


【思路】F[i][j]表示串X匹配到I位,串Y匹配到J位的最长公共子序列长度,递归输出方案

/*最长公共子序列 输方案*/ 
#include<cstdio>
#include<cstring>
using namespace std;
const int maxlen=200;
char s1[maxlen],s2[maxlen];
int dp[maxlen+1][maxlen+1];
int i,j;
char ans[maxlen];

int MAX(int &a,int b,int c,int d)
{
   if (b>a) a=b;
   if (c>a) a=c;
   if (d>a) a=d;
} 
int main()
{
    freopen("lcs.in","r",stdin);
    freopen("lcs.out","w",stdout); 
    scanf("%s",&s1);
    scanf("%s",&s2);
    int same;
    int len1=strlen(s1)-1; 
    int len2=strlen(s2)-1;
   
    for (i=0;i<=len1;i++)
     for (j=0;j<=len2;j++)
     {
       dp[i][j]=0;
      if (s1[i]==s2[j]) same=1; else same=0;
      MAX(dp[i][j],dp[i-1][j],dp[i][j-1],dp[i-1][j-1]+same);//DP方程
      }
    printf("%d\n",dp[len1][len2]);
    
    int top=0;
    i=len1; j=len2;
    while (i>-1 && j>-1)
     {
        if (s1[i]==s2[j]) 
         {
           ans[++top]=s1[i];
           i--;
           j--;
           }
        else {
               if (dp[i-1][j]>dp[i][j-1])  i--; 
               else j--;
             }
     }        
    for (i=top;i>0;i--) printf("%c",ans[i]);
    printf("\n");                         
   // getchar();
  // getchar();
}
    
    


11、编辑距离(editor)

【试题】字符串是数据结构和计算机语言里很重要的数据类型,在计算机语言中,对于字符串我们有很多的操作定义,因此我们可以对字符串进行很多复杂的运算和操作。实际上,所有复杂的字符串操作都是由字符串的基本操作组成。例如,把子串a替换为子串b,就是用查找、删除和插入这三个基本操作实现的。因此,在复杂字符串操作的编程中,为了提高程序中字符操作的速度,我们就应该用最少的基本操作完成复杂操作。在这里,假设字符串的基本操作仅为:删除一个字符、插入一个字符和将一个字符修改成另一个字符这三种操作。

我们把进行了一次上述三种操作的任意一种操作称为进行了一步字符基本操作。

下面我们定义两个字符串的编辑距离:对于两个字符串a和b,通过上述的基本操作,我们可以把a变成b或b变成a;那么,把字符串a变成字符串b需要的最少基本字符操作步数称为字符串a和字符串b的编辑距离。

例如,如a=“ABC”,b=“CBCD”,则a与b的编辑距离为2。

你的任务就是:编一个最快的程序来计算任意两个字符串的编辑距离。

输入:第1行为字符串a;第2行为字符串b。注:字符串的长度不大于10000,字符串中的字符全为大写字母。

输出:编辑距离。

输入输出示例:

INPUT2.TXT

ABC

CBCD

OUTPUT2.TXT

2

【思路】f[i,j]表示a的前i个字符到b的前j个字符的编辑距离,具体看注释

#include<cstdio>
#include<cstring>
char a[6000],b[6000];
int f[6000][6000];
int i,j;
int main()
{
    freopen("editor.in","r",stdin);
    freopen("editor.out","w",stdout);
    scanf("%s",&a);
    scanf("%s",&b);
    int min;
    int lena=strlen(a);
    int lenb=strlen(b);
    int max=lena>lenb?lena:lenb;
    for (i=1;i<=max;i++) { f[i][0]=i; f[0][i]=i; } //注意初始化
    for (i=1;i<=lena;i++)
      for (j=1;j<=lenb;j++)
       {           
         min=10001;                 
         if (a[i-1]==b[j-1]) min=f[i-1][j-1];//C++字符数组下标从0开始 所以判断的是A[I-1]和B[J-1] 相同这一位就不用做
         else {
               if (f[i-1][j]+1<min) min=f[i-1][j]+1;
               if (f[i-1][j-1]+1<min) min=f[i-1][j-1]+1;
               if (f[i][j-1]+1<min) min=f[i][j-1]+1;//不用管具体怎么操作,转移都是从这三个方向来的
               }
         f[i][j]=min;
          } 
    printf("%d\n",f[lena][lenb]);
    //getchar();
   // getchar();
}     


12、字串距离

源程序名       blast.???(pas, c, cpp)       可执行文件名   blast.exe

输入文件名     blast.in                     输出文件名     blast.out

【问题描述】

       设有字符串X,我们称在X的头尾及中间插入任意多个空格后构成的新字符串为X的扩展串,如字符串X为”abcbcd”,则字符串“abcb□cd”,“□a□bcbcd□”和“abcb□cd□”都是X的扩展串,这里“□”代表空格字符。

       如果A1是字符串A的扩展串,B1是字符串B的扩展串,A1与B1具有相同的长度,那么我扪定义字符串A1与B1的距离为相应位置上的字符的距离总和,而两个非空格字符的距离定义为它们的ASCII码的差的绝对值,而空格字符与其他任意字符之间的距离为已知的定值K,空格字符与空格字符的距离为0。在字符串A、B的所有扩展串中,必定存在两个等长的扩展串A1、B1,使得A1与B1之间的距离达到最小,我们将这一距离定义为字符串A、B的距离。

       请你写一个程序,求出字符串A、B的距离。

【输入】

       输入文件第一行为字符串A,第二行为字符串B。A、B均由小写字母组成且长度均不超过2000。第三行为一个整数K(1≤K≤100),表示空格与其他字符的距离。

【输出】

       输出文件仅一行包含一个整数,表示所求得字符串A、B的距离。

【样例】

       blast.in                                       blast.out

       cmc                                           10

       snmn

       2

 

【思路】和上一题类似

#include<cstdio>
#include<cstring>
#include<cmath>
#include<climits>
using namespace std;

char a[2001],b[2001];
int i,j,k;
long f[2000][2000];
    
int main()
{
    freopen("blast.in","r",stdin);
    freopen("blast.out","w",stdout);
    scanf("%s",&a);
    scanf("%s",&b);
    scanf("%d",&k);
    int lena=strlen(a);
    int lenb=strlen(b);
    int min;
    int max=lena>lenb?lena:lenb;
    for (i=1;i<=max;i++)
     {
        f[i][0]=i*k;
        f[0][i]=i*k;
        }//注意初始化
     for (i=1;i<=lena;i++)
       for (j=1;j<=lenb;j++)
        {
           if (a[i-1]!=b[j-1])
           {                 
             min=INT_MAX;
             if (f[i-1][j]+k<min) min=f[i-1][j]+k;
             if (f[i][j-1]+k<min) min=f[i][j-1]+k;
             int cost=a[i-1]-b[j-1]>0?a[i-1]-b[j-1]:b[j-1]-a[i-1];
             if (f[i-1][j-1]+cost<min) min=f[i-1][j-1]+cost;                               
             }
             else min=f[i-1][j-1];
           f[i][j]=min;
        }
     printf("%d\n",f[lena][lenb]);
   //  getchar();  getchar();
     
}     

你可能感兴趣的:(【线性DP】基础练习)