动态规划

最长子序列问题

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

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

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

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

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

输出样例:
6

最长公共子序列(LCS)

例如:有字符串s1,s2,按下列方式填表
动态规划得到公式:
dp[m][n]=dp[m1][n1]+1;(a[m1]==b[n1])dp[m][n] = dp[m - 1][n - 1] + 1; (a[m - 1] == b[n - 1])
dp[m][n]=max(dp[m1][n],dp[m][n1]);(a[m1]!=b[n1])dp[m][n] = max(dp[m - 1][n], dp[m][n - 1]); (a[m - 1] != b[n - 1])
可求出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[m1][n]+a[m][n1]a[m][n] = a[m-1][n] +a[m][n-1]
按照此规律递归填数就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];
}

进阶路径规划

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