算法课实验

来源:互联网 发布:淘宝卖蜂蜜需要什么 编辑:程序博客网 时间:2024/05/22 03:58

LCS的实验,注释写的比较详细.

#include <stdio.h>#include <string.h>char a[505];//第一个序列char b[505];//第二个序列int dp[505][505];//dp[i][j]是在第一个序列的第i个字母和第二个序列的第j个字母时的最长公共子序列char result[505];//存储解int num;//存储解的个数int main(){     int i,j,n,l1,l2;     printf("请输入第一个序列\n");     scanf("%s",a+1);//这里注意由于下标从0开始所以第一个位置空下来否则dp[i-1][j-1]就会越界     printf("请输入第二个序列\n");     scanf("%s",b+1);     l1=strlen(a+1);     l2=strlen(b+1);     memset(dp,0,sizeof(dp));//dp数组清零     for (i=1;i<=l1;i++)        for (j=1;j<=l2;j++)         if (a[i]==b[j])//如果第一个序列的第i个字母等于第二个序列的第j个字母          dp[i][j]=dp[i-1][j-1]+1;           else//如果这两个位置的字母不等,则取dp[i-1][j]和dp[i][j-1]中较大的作为dp[i][j]           {              if (dp[i-1][j]>dp[i][j-1])               dp[i][j]=dp[i-1][j];              else               dp[i][j]=dp[i][j-1];           }           printf("最长公共子序列的长度为:%d\n",dp[l1][l2]);    if (dp[l1][l2]!=0)    {     printf("构造的一个解为:%");    //从后面往前面推出解,因为dp[i][j]存储的是上一次到这一次所以有这次的dp[i][j]可得上一次是怎样来的    i=l1;    j=l2;    num=0;    while(i>=1&&j>=1)    {      if (a[i]==b[j])      {        result[num++]=a[i];        i--;        j--;      }      else        if (dp[i-1][j]>dp[i][j-1])         i--;      else         j--;    }    for (i=num-1;i>=0;i--)//最后要逆序输出    printf("%c",result[i]);    }    else    {    printf("无构造解!\n");    }   return 0; }

打印全部的最长公共子序列的代码,就是直接在输出时加了一个回溯,但是有些小问题,有时候会重复输出,目前能想到的办法就是放入字符串数组中来判重再输出,比如一条字符串为:bacdca,另一条为:abdca会重复输出.

#include <stdio.h>#include <string.h>char a[505];//第一个序列char b[505];//第二个序列int dp[505][505];//dp[i][j]是在第一个序列的第i个字母和第二个序列的第j个字母时的最长公共子序列char p[505];int num;//存储解的个数int l1,l2;void lcs(int n,int m,char s[],int k);//用来找到所有最长公共子序列int main(){     int i,j,n;     printf("请输入第一个序列\n");     scanf("%s",a+1);//这里注意由于下标从0开始所以第一个位置空下来否则dp[i-1][j-1]就会越界     printf("请输入第二个序列\n");     scanf("%s",b+1);     l1=strlen(a+1);     l2=strlen(b+1);     memset(dp,0,sizeof(dp));//dp数组清零     for (i=1;i<=l1;i++)        for (j=1;j<=l2;j++)         if (a[i]==b[j])//如果第一个序列的第i个字母等于第二个序列的第j个字母          dp[i][j]=dp[i-1][j-1]+1;           else//如果这两个位置的字母不等,则取dp[i-1][j]和dp[i][j-1]中较大的作为dp[i][j]           {              if (dp[i-1][j]>dp[i][j-1])               dp[i][j]=dp[i-1][j];              else               dp[i][j]=dp[i][j-1];           }           printf("最长公共子序列的长度为:%d\n",dp[l1][l2]);    if (dp[l1][l2]!=0)    {     printf("构造的解为:\n");    //从后面往前面推出解,因为dp[i][j]存储的是上一次到这一次所以有这次的dp[i][j]可得上一次是怎样来的     lcs(l1,l2,p,dp[l1][l2]);    }    else    {    printf("无构造解!\n");    }    return 0;}void lcs(int n,int m,char s[],int k){    int i;    if ((k<1)||(n<1)||(m<1))//找到解了    {        for (i=1;i<=dp[l1][l2];i++)        printf("%c",s[i]);        printf("\n");        return;    }    else    {        if (a[n]==b[m])        {            s[k--]=a[n];            lcs(n-1,m-1,s,k);        }        else        if (dp[n-1][m]>dp[n][m-1])        {            lcs(n-1,m,s,k);        }        else        if (dp[n][m-1]>dp[n-1][m])        {            lcs(n,m-1,s,k);        }        else        {            lcs(n-1,m,s,k);            lcs(n,m-1,s,k);        }    }}



还有书后练习题的代码,关于硬币找零的问题,稍微注意下初始化。

#include <stdio.h>#include <string.h>int min(int a,int b){    return a<b?a:b;} int main() {     int n,m,i,j,k,ans;     int v[505];     int dp[505][505];     printf("请输入硬币的种类\n");     scanf("%d",&n);     printf("请依次输入硬币面值\n");     for (i=1;i<=n;i++)     scanf("%d",&v[i]);     printf("待分配的总钱数\n");     scanf("%d",&m);     for (i=0;i<=n;i++)        for (j=0;j<=m;j++)        dp[i][j]=99999;     for (i=0;i<=m;i++)      dp[i][0]=0;//当钱数为0时一律为0     for (i=1;i<=n;i++)        for (j=0;j<=m;j++)         {            ans=9999;           for (k=0;k<=j/v[i];k++)           ans=min(ans,dp[i-1][j-k*v[i]]+k);           dp[i][j]=ans;          }          if (dp[n][m]!=9999)          printf("最少个数为:%d\n",dp[n][m]);          else            printf("无法找钱\n");          return 0; }




0 0