算法课实验

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值