动态规划

最长子序列问题

最长上升子序列及最长下降子序列

某国为了防御敌国的导弹袭击,开发出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭,并观测到导弹依次飞来的高度,请计算这套系统最多能拦截多少导弹。拦截来袭导弹时,必须按来袭导弹袭击的时间顺序,不允许先拦截后面的导弹,再拦截前面的导弹。

输入格式:
第一行,输入雷达捕捉到的敌国导弹的数量k(k<=25),第二行,输入k个正整数,表示k枚导弹的高度,按来袭导弹的袭击时间顺序给出,以空格分隔。

输出格式:
输出只有一行,包含一个整数,表示最多能拦截多少枚导弹。

输入样例:
8
300 207 155 300 299 170 158 65

输出样例:
6

最长公共子序列(LCS)

例如:有字符串s1,s2,按下列方式填表
在这里插入图片描述得到公式:
d p [ m ] [ n ] = d p [ m − 1 ] [ n − 1 ] + 1 ; ( a [ m − 1 ] = = b [ n − 1 ] ) dp[m][n] = dp[m - 1][n - 1] + 1; (a[m - 1] == b[n - 1]) dp[m][n]=dp[m1][n1]+1;(a[m1]==b[n1])
d p [ m ] [ n ] = m a x ( d p [ m − 1 ] [ n ] , d p [ m ] [ n − 1 ] ) ; ( a [ m − 1 ] ! = b [ n − 1 ] ) dp[m][n] = max(dp[m - 1][n], dp[m][n - 1]); (a[m - 1] != b[n - 1]) dp[m][n]=max(dp[m1][n],dp[m][n1]);(a[m1]!=b[n1])
可求出LCS长度。
之后利用回溯法,斜上方的方格中,s1元素等于s2,则跳到斜上方方格,否则向左或者向上走。可求出序列(不一定唯一,只能求出其中一个)

例:两种水果杂交出一种新水果,现在给新水果取名,要求这个名字中包含以前两种水果的字母,且名字尽量短,即:以前的水果名字arr1、arr2是新水果名arr的子序列,使用动态规划的思想设计算法得到新水果名arr。
输入格式:
以空格分开两个水果的名字
输出格式:
新水果的名字
输入样例:
apple peach
输出样例:
appleach

代码如下:

#include<stdio.h>
#include<string.h>

/**
求最大值
**/
int max(int a, int b) {
	if (a > b) {
		return a;
	}
	else {
		return b;
	}
}


/**
动态规划求最长公共子序列
**/
void Lcs(char a[], char b[], int dp[][10]) {
	int a_length, b_length;
	a_length = strlen(a);
	b_length = strlen(b);//求出两个字符串的长度
	memset(dp, 0, sizeof(dp));
	for (int m = 1; m <= a_length; m++) {//填写m+1行n+1列数组
		for (int n = 1; n <= b_length; n++) {
			if (a[m - 1] == b[n - 1]) {
				dp[m][n] = dp[m - 1][n - 1] + 1;
			}
			else {
				dp[m][n] = max(dp[m - 1][n], dp[m][n - 1]);
			}
		}
	}
}

/**
构建新水果名
**/
int FindLcs(char a[], char b[], int dp[][10],char c[]) {
	int i, j ,z= 0;
	i = strlen(a); j = strlen(b);
	while (i != 0 && j != 0) {
		if (a[i - 2] == b[j - 2]) {	
			c[z] = a[i-1];
			i--; j--; z++;
		}
		else if (dp[i - 1][j] < dp[i][j - 1]) {
			c[z] = b[j-1];
			j--; z++;
		}
	
		else if (dp[i][j - 1] <= dp[i - 1][j]) {
			c[z] = a[i-1];
			i--; z++;
		}
		
	}
	return z;
}


int main() {
	char a[10]; char b[10]; char c[20];
	int i = 0; int a_length = strlen(a);
	int b_length = 0;
	int dp[10][10];
	scanf("%s", &a);
	scanf("%s", &b);
	Lcs(a, b, dp);
	int c_length = FindLcs(a, b, dp,c);
	for (int z = c_length -1; z >= 0; z--) {
		printf("%c", c[z]);
	}
	return 0;
}

机器人问题

简单路径规划

规定方向寻找路线条数
在这里插入图片描述
如图,从吃冰激凌的皮卡丘走到吃苹果的皮卡丘需要两步:
1、吃冰激凌的皮卡丘------>吃棒棒糖的皮卡丘/喝咖啡的皮卡丘
2、吃棒棒糖的皮卡丘/喝咖啡的皮卡丘------->吃苹果的皮卡丘
公式如下:
a [ m ] [ n ] = a [ m − 1 ] [ n ] + a [ m ] [ n − 1 ] a[m][n] = a[m-1][n] +a[m][n-1] a[m][n]=a[m1][n]+a[m][n1]
按照此规律递归填数就ok。
例:一个机器人只能向下和向右移动,每次只能移动一步,设计一个算法求机器人从(0,0)到(m,n)有多少条路径。(目前有个问题,为什么走到(4,5)是35,我觉得是(3,4)是35)

在这里插入图片描述

输入格式:

以空格分开m,n

输出格式:

路径条数

输入样例:

4 5

输出样例:

35

int lujing(int m,int n) {
    
    int i,j;
    for(i=1;i<=m;i++) {
        for(j=1;j<=n;j++) {
            a[i][j] = a[i-1][j] + a[i][j-1];
        }
    }
    return a[m-1][n-1];
}

进阶路径规划

除了规定方向还要设置雷区
可考虑把所有路径列出来再删去以禁区为起点到终点的路径(还未实现)

最大乘积问题

I是一个n位十进制整数,要将I划分为k段,找出得到的k个整数的最大乘积,即:
在I中插入k个乘号,使所得积最大。根据DP思想,需要先找到前i位,插入k-1个乘号使得积最大,然后再乘以剩下的位数,找出最大值。
设计方案如下:
设两个二维数组,m[i][k],w[i][i].其中m[i][k]表示从第一位数到第i位数的划分为k个整数的最大乘积,w[i][i]表示i+1到剩下的数字。

得到如下方程:

在这里插入图片描述
其中: 1<=d< i (即从1开始一直到i-1 中找最大值)

1、 输入I,k并用一维数组保存I
2、 初始化二维数组w和m
3、 利用DP方程进行求解
代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXN 50
#define MAXK 10

long m[MAXK][MAXN] = { {0,0} }; /*初始化操作*/
long w[MAXN][MAXN] = { {0,0} };

void maxdp(int n, int k, int *a)
{
	int i, j, d, h, q, t, s;
	long temp, max;
	for (i = 1; i <= n; i++) /*分成1段*/
		m[i][1] = w[1][i];
	for (i = 1; i <= n; i++) /* DP 过程*/
		for (j = 2; j <= k; j++)
		{
			max = 0;
			for (d = 1; d < i; d++)
				if ((temp = m[d][j - 1] * w[d + 1][i]) > max)
					max = temp;
			m[i][j] = max;
		}
}

int main()
{
	int n, k, i, j;
	int a[MAXN] = { 0 }, la = 0;
	char c;
	scanf("%d %d ", &n, &k);

	while ((c = getchar()) != ' ') /*读入数据*/
	{
		a[++la] = c - '0';
	}

	for (i = 1; i <= n; i++)
	{
		w[i][i] = a[i];
		for (j = i + 1; j <= n; j++)
			w[i][j] = w[i][j - 1] * 10 + a[j];
	}
	maxdp(n, k, a);
	printf("%ld ", m[n][k]);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值