《算法分析与设计》练习10

目录

A 最长递增子序列

B 构造最长递增子序列

C 0-1背包问题

D x星人的基因

E 出列人数

F XP的午餐


A 最长递增子序列

题目描述

给出一个序列a1,a2,a3,a4,a5,a6,a7...an,求它的一个子序列(设为s1,s2,...sn),使得这个子序列满足这样的性质:s1<s2<s3<...<sn并且这个子序列的长度最长。输出这个最长子序列的长度,要求时间复杂度为O(n2)。

输入

每组输入包括两行,第一行为序列长度n,第二行为序列。

输出

输出最长递增子序列的长度。

样例输入 Copy

7

1 7 3 5 9 4 8

样例输出 Copy

4

分析:这题需要用一个一维数组来记录,每次的最长递增序列长度,并找到最大值即可。

          具体还是看代码:

代码实现:c语言

#include <stdio.h>

#include <stdlib.h>



int main (){

int a[2000];

int b[2000];

int n;

while(~scanf("%d",&n)){

    for(int i=0;i<n;i++)

        scanf("%d",&a[i]);

    int z=solve(a,b,n);

    printf("%d",z);

    printf("\n");



}





return 0;

}





int solve(int a[],int b[],int n){

b[0]=1;

int maxlen;

int max=b[0];

for(int i=1;i<n;i++)

{

    maxlen=0;

    for(int j=i-1;j>=0;j--)

    {

        if(a[j]<a[i]&&maxlen<b[j])

        {

            maxlen=b[j];

        }

    }

    b[i]=maxlen+1;

    if(b[i]>max)

        max=b[i];

}

return max;

}

B 构造最长递增子序列

题目描述

在“最长递增子序列”的基础上对代码进行改进,输出一条最长递增子序列。

输入

每组输入包括两行,第一行为序列长度n,第二行为序列。

输出

输出最长递增子序列中的任意一条即可。

样例输入 Copy

7

1 7 3 4 9 2 3

样例输出 Copy

1 3 4 9

分析:在第一题的基础上,做一点点记录即可,比如:用一个一维数组来记录每次上一次的最大值的下标。

直接看代码分析:

代码实现:c语言

#include <stdio.h>

#include <stdlib.h>



int p[2000];



int main (){

int a[2000];

int b[2000];

int n;

while(~scanf("%d",&n)){

    for(int i=0;i<n;i++)

        scanf("%d",&a[i]);

    int z=solve(a,b,n);

    printf("\n");



}





return 0;

}





int solve(int a[],int b[],int n){

b[0]=1;

int maxlen;

int max=b[0];

int x=0;

for(int i=1;i<n;i++)

{

    maxlen=0;

    for(int j=i-1;j>=0;j--)

    {

        if(a[j]<a[i]&&maxlen<b[j])

        {

            maxlen=b[j];

            p[i]=j;

        }

    }

    b[i]=maxlen+1;

    if(b[i]>max){

        max=b[i];

        x=i;

    }

}

int i=x;

int m=max;

int c[2000];



while(m>0){

    c[m]=a[i];

    i=p[i];

    m--;

}

for(int i=1;i<=max;i++)

    printf("%d ",c[i]);

return max;

}

C 0-1背包问题

题目描述

给定n种物品和一个背包,物品i的重量是Wi,其价值为Vi,背包的容量为C。如何选择装入背包的物品,可以使得装入背包中物品的总价值最大?

输入

每组输入包括三行,

第一行包括物品个数n,以及背包容量C。

第二、三行包括两个一维数组,分别为每一种物品的价值和重量。

输出

输出包括两行,第一行为背包的最大总价值,第二行为所选取的物品。

例如:最大总价值=15,物品选取策略为11001。数据保证答案唯一。

样例输入 Copy

5 10

6 3 5 4 6

2 2 6 5 4

样例输出 Copy

15

11001

分析:0-1背包问题是经典的填表问题,动态规划。

先填最下层,再依次往上面填。

设:tb[i][j] ,其中i表示第 i 个物品 ,j 表示 还剩下多少容量。而tb[i][j]本身代表 已经装了的物品的总价值。

所以可以 在 填第 最后一个物品时 ,也就是 i=n-1。

所以   if  j  <  w[n-1]      tb[n-1][j]=0 ,   也就是 装不进。

         if  j>= w[n-1]   tb[n-1][j] = v[n-1] 

然后,如果 当 i<=n-2 时

          if  j>=  w[i]  tb[i][j] = max (  tb[i+1][j]  ,  tb[i+1][j-w[i]] +v[i]  );  (取两者当中得最大值)

          if  j< w[i]   tb[i][j] = tb[i+1][j]; (直接取下一个背包);

所以代码分析得:

代码实现:c语言

#include <stdio.h>

#include <stdlib.h>



int tb[200][200];

int main (){

int w[200];

int v[200];

int n;

int c;

while(~scanf("%d %d",&n,&c)){

    for(int i=0;i<n;i++)

        scanf("%d",&v[i]);

    for(int i=0;i<n;i++)

        scanf("%d",&w[i]);

    int z=solve(w,v,n,c);

    printf("%d",z);

    printf("\n");

    traceback(w,c,n);

    printf("\n");

}





return 0;

}



int solve(int w[],int v[],int n,int c){

    int jmax = min(c,w[n-1]-1);

    for(int j=0;j<=jmax;j++)

        tb[n-1][j]=0;

    for(int j=jmax+1;j<=c;j++)

        tb[n-1][j]=v[n-1];

    for(int i=n-2;i>=0;i--)

    {

        int jmax = min(c,w[i]-1);

        for(int j=0;j<=jmax;j++)

            tb[i][j]=tb[i+1][j];



        for(int j=jmax+1;j<=c;j++)

            tb[i][j]=max(tb[i+1][j],tb[i+1][j-w[i]]+v[i]);



    }



return tb[0][c];

}



int max(int x,int y){

return x>y?x:y;



}



int min(int x,int y){

    return x<y?x:y;

}





void traceback(int w[],int c,int n){

    for(int i=0;i<n;i++){

        if(tb[i][c]==tb[i+1][c]){

            printf("0");



        }

    else{

        c-=w[i];

        if(c>=0)

        printf("1");

        else{

            printf("0");

        }

    }

    }



}

D x星人的基因

题目描述

X星人的基因由A、B、C、D、E五种不同的结构组合而成。

如果两个性别不同的X星人的基因序列相似度大于50%,按照X星的法律他们是禁止结婚的,等于50%据说还是可以的。

那么基因的相似度怎么计算呢?分别从两个人身上取长度均为N的基因片段,如果它们的最长公共子序列为M,则相似度=M/N。是不是很简单呢?

现在给你两段X星人的基因序列片段,请你判断他们是不是可以结婚?

输入

每一组测试数据包含3行,

第1行数字N表示待比较基因序列片段的长度,N<=10^3。

第2行和第3行为两个长度为N的基因序列片段。

输入0表示结束。

输出

两个X星人是否可以结婚,如果可以输出”Yes“,如果不可以输出”No“。

样例输入 Copy

8

A B C D E A B C

A C C D C B A E

6

A B C D E E

A E D C B B

0

样例输出 Copy

Yes

Yes

分析:此题其实题目很简单,就是 把最长公共子序列的长度求出来即可。

          我们还是动态规划来写。

这题要注意的是,我们如何去读入这两行字符串。

具体请看代码分析:

代码实现:c语言

#include <stdio.h>

#include <stdlib.h>



int tb[2000][2000];

int main (){



int n;

char a[2000];

char b[2000];

while(~scanf("%d",&n)){

        if(n==0) break;

    getchar();

    for(int i=0;i<n;i++)

    {

        scanf("%c",&a[i]);

        getchar();

    }

    for(int i=0;i<n;i++)

    {

        scanf("%c",&b[i]);

        getchar();

    }

    int z=solve(a,b,n);

    double p=z*1.0/n;

    if(p-0.5<=0) printf("Yes\n");

    else printf("No\n");



}





return 0;

}



int solve(char a[],char b[],int n){

  for(int i=1;i<=n;i++)

    for(int j=1;j<=n;j++)

  {

      if(a[i-1]==b[j-1])

        tb[i][j]=tb[i-1][j-1]+1;

      else{

        if(tb[i][j-1]>tb[i-1][j])

            tb[i][j]=tb[i][j-1];

        else tb[i][j]=tb[i-1][j];

      }



  }



  return tb[n][n];

}

E 出列人数

(很抱歉了,没有第5题,学校oj没开,但是题目其实就是先把最长递增子序列求出来,然后用总数去减即可)

F XP的午餐

题目描述

XP每天都会思考一个问题,今天午餐去哪里吃?这是一个很重要的问题,这会影响到他下午的体力值。他的午餐预算是M元,现在有N种菜品,每一种菜品的价格和能够提供的体力值已知(每种菜品只能选择一次),请问如何选择菜品能够让XP下午的体力值最大呢?

输入

多组输入

第一行:M元和菜品数量N。

接下来N行,每一行两个整数,分别表示每一种菜品的价格(vi)和能够获得的体力值(wi)。

(0<N<=20,0<=M<=1000)(0<=vi<=50,0<=wi<=100)

输出

最大体力值。

样例输入 Copy

10 5

1 5

2 4

3 3

4 2

5 1

样例输出 Copy

14

分析:这题其实就是一个变相的0-1背包问题, 用刚刚好的钱,获得最大的体力值。

只需在之前的0-1背包上,把v[i]与w [i]的功能换一下,c变成m即可。

这里就不作详细分析啦

请直接看代码分析:

代码实现:c语言

#include <stdio.h>

#include <stdlib.h>



int tb[2000][2000];

int main (){

int v[2000];

int w[2000];

int m;

int n;

while(~scanf("%d %d",&m,&n)){

    for(int i=0;i<n;i++)

        scanf("%d %d",&v[i],&w[i]);

    int z=solve(v,w,m,n);

    printf("%d",z);

    printf("\n");

}





return 0;

}



int solve(int v[],int w[],int m,int n){

int jmax=min(m,v[n-1]-1);

for(int j=0;j<=jmax;j++)

    tb[n-1][j]=0;

for(int j=jmax+1;j<=m;j++)

    tb[n-1][j]=w[n-1];

for(int i=n-2;i>=0;i--)

{

    int jmax=min(m,v[i]-1);

    for(int j=0;j<=jmax;j++)

        tb[i][j]=tb[i+1][j];

    for(int j=jmax+1;j<=m;j++)

        tb[i][j]=max(tb[i+1][j],tb[i+1][j-v[i]]+w[i]);



}





return tb[0][m];

}



int max(int x,int y){

return x>y?x:y;

}



int min(int x,int y){



return x<y?x:y;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值