动态规划 dp02 最长非降子序列问题 c代码

先看下题目:

给定一个由n个正整数组成的序列,从该序列中删除若干个整数,使剩下的整数组成非降子序列,求
最长的非降子序列。
例如,由12个正整数组成的序列为:
    48,16,45,47,52,46,36,28,46,69,14,42
请在序列中删除若干项,使剩下的项为非降(即后面的项不小于前面的项)序列,剩下的非降序列
最多为多少项?

这道题第一次做是会做的,刷了两天动态规划类题目,第二次做的时候,不会了 ( ̄_, ̄ ),难道dp类题目刷的不够多?

我们知道,动态规划类题目通常分为三个步骤求解:

1. 分阶段。

2. 状态迁移方程。

3. 求取最优解。

第一次做思考了一下就想到方案了,第二次做的时候没思考就想着套动态规划的解题过程,结果把自己套没了...... 看来解决问题不能套公式,灵活处理最重要。

对于这套题目,设数组a[n], 假设M[i]表示从i~n的最长非降数列长度,边界情况是m[n] = 1;从后往前遍历,求m[i],对于任意一个j, i < j < n;

如果a[i] <= a[j],则m[i] = m[j] + 1; 遍历[i+1, n],找到最大的m[i]即可。

状态迁移方程得到了,求取最优解序列的时候,遍历m[i],找到最大值i,顺着i打印即可。保持后续的打印数字m[i]递减。

这道题的c代码:

/*
 *  最长非降子序列
 *  
 *  m[i] = MAX(m[j]) + 1; a[i] > a[j] && j < i;
 */

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

#define MAX(a, b) ((a) > (b) ? (a) : (b))

void main()
{
	int a[30] = {0}, n, i,j,k,max,m[30] = {0}, c[30];

	printf("input total numbers :"); scanf("%d", &n);

	if (n > 30)
	{
		printf("invalid number\n");
		return;
	}

	//随机生成一些数字
	srand(time(NULL));
	for (i = 0; i < n; i++)
		a[i] = rand()%20 + 1;

	printf("无序数列为:");
	for (i = 0; i < n; i++)
		printf("%d ", a[i]);
	printf("\n");	
		

	//边界初始化
	m[n-1] = 1;
	c[n-1] = n-1;

	//状态递推
	for (i = n- 2; i >= 0; i--)
	{
		m[i] = 1;
		c[i] = i;
		
		for (j = n - 1; j > i; j--)
		{
			if (a[i] <= a[j])
			{
				if (m[i] < m[j] + 1){
					m[i] = m[j] + 1;	
					c[i] = j;
				}
			}	
		}
	}

	//打印最优值
	max = 0;
	for (i = 0; i < n; i++){	
		//printf("%d ", m[i]);
		if (m[i] > max)
		{
			max = m[i];
			k = i;
		}
	}	
		
	printf("最长非降子数列长度为:%d\n", max);
	while (c[k] != k)
	{
		printf("%d ", a[k]);
		k = c[k];
	}
	printf("%d \n",a[k]);
	return;
}

 

参考资料:

1. 数据结构 : C语言版/ 严蔚敏,吴伟民编著

=============================================================================================

Linux应用程序、内核、驱动开发交流讨论群(745510310),感兴趣的同学可以加群讨论、交流、资料查找等,前进的道路上,你不是一个人奥^_^。
 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是使用动态规划算法实现最长公共子序列问题,并输出最长公共子序列的C语言代码: ```c #include <stdio.h> #include <string.h> #define MAXLEN 100 void LCS(char *str1, char *str2, int len1, int len2, int dp[MAXLEN][MAXLEN]) { // 计算dp数组 for (int i = 0; i <= len1; i++) { for (int j = 0; j <= len2; j++) { if (i == 0 || j == 0) { dp[i][j] = 0; } else if (str1[i - 1] == str2[j - 1]) { dp[i][j] = dp[i - 1][j - 1] + 1; } else { dp[i][j] = (dp[i - 1][j] > dp[i][j - 1]) ? dp[i - 1][j] : dp[i][j - 1]; } } } // 回溯找到最长公共子序列 int index = dp[len1][len2]; char lcs[index + 1]; lcs[index] = '\0'; int i = len1, j = len2; while (i > 0 && j > 0) { if (str1[i - 1] == str2[j - 1]) { lcs[index - 1] = str1[i - 1]; i--; j--; index--; } else if (dp[i - 1][j] > dp[i][j - 1]) { i--; } else { j--; } } // 输出最长公共子序列 printf("最长公共子序列为:%s\n", lcs); } int main() { char str1[MAXLEN], str2[MAXLEN]; int dp[MAXLEN][MAXLEN] = {0}; printf("请输入第一个字符串:"); scanf("%s", str1); printf("请输入第二个字符串:"); scanf("%s", str2); int len1 = strlen(str1); int len2 = strlen(str2); LCS(str1, str2, len1, len2, dp); return 0; } ``` 该代码在计算dp数组的同时,通过回溯的方式找到最长公共子序列。具体实现方式是,从dp数组的右下角开始,若`str1[i-1]`与`str2[j-1]`相等,则将该字符加入到最长公共子序列中,同时向左上角移动一格;否则,若`dp[i-1][j]`大于`dp[i][j-1]`,则向上移动一格,否则向左移动一格。最后输出最长公共子序列即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值