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;
}