动态规划题解——最长子序列专题

A. 拍照

Description

最近,文明班级需要拍集体照,N位同学站成一排,为了美观,老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成中间高两边低的队形。这种队形是:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK, 则他们的身高满足T1<…Ti+1>…>TK(1<=i<=K)。你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成这种队形。

Input

输入的第一行是一个整数N(2<=N<=100),表示同学的总数。第二行有n个整数,用空格分隔,第i个整数Ti (130<= Ti <=230)是第i位同学的身高(厘米)。

Output

输出包括一行,这一行只包含一个整数,就是最少需要几位同学出列。

Sample Input

8
186 186 150 200 160 130 197 220

Sample Output

4

Answer

#include<bits/stdc++.h>
using namespace std;

int a[10001],dp[10001],indexs[10001];
int main() {
	int i,j,k,n,mx,index,indexCnt;
	while(1==scanf("%d",&n)) {
		for(i=1; i<=n; i++) {
			scanf("%d",&a[i]);
		}
		for(i=1; i<=n; i++) {
			dp[i]=1;
		}
		mx=1;
		index=n;
		indexCnt=0;
		indexs[++indexCnt]=index;
		for(i=n-1; i>=1; i--) {
			for(j=i+1; j<=n; j++) {
				if(a[i]>a[j]&&dp[i]<dp[j]+1) {
					dp[i]=dp[j]+1;
				}
			}
			if(mx<dp[i]) {
				mx=dp[i];
				index=i;
				indexs[++indexCnt]=index;
			}
		}
		index=4;
		for(k=1; k<=indexCnt; k++) {
			index=indexs[k];
			for(i=index-1; i>=1; i--) {
				for(j=i+1; j<=n; j++) {
					if(a[i]<a[j]) {
						dp[i]=max(dp[i],dp[j]+1);
					}
				}
				mx=max(mx,dp[i]);
			}
		}
		printf("%d\n",n-mx);
	}
	return 0;
}

B. 数字三角形问题A

Description

设有一个三角形的数塔,顶点结点称为根结点,每个结点有一个整数数值。从顶点出发,可以向左走,也可以向右走。如图所示:
在这里插入图片描述

​ 当三角形的数塔给定之后,找出一条从第一层到达底层的路径,使路径的值(路径上的圈内的数字之和)最小。

Input

本问题有多组测试数据,第一行就是测试数据的组数,对于每一组测试数据,共有两部分,其中第一部分是一行,即层数L(1<=L<=100),第二部分有L行,每行有L个数字,0<=数字<=1000,数字之间有空格。

Output

对于每一组测试数据,输出只有一行,就是最小的路径值。

Sample Input

1
5
13
11 8
12 7 26
6 14 15 8
12 7 13 24 11

Sample Output

49

Answer

#include<bits/stdc++.h>
using namespace std;

int n,a[100][100],f[100][100],ans,repeat;
int main() {
	scanf("%d",&repeat);
	while(repeat--) {
		scanf("%d",&n);
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <=i; j++)
				scanf("%d",&a[i][j]);
		//第一排的第一个数字用于作为动态规划的基础 
		f[1][1] = a[1][1]; 
		for (int i = 2; i <= n; i++)
			for (int j = 1; j <=i; j++) {
				//如果是这一行的第一个数,则与上一行第一个的最优解相加 
				if(j==1){
					f[i][j]=f[i-1][j]+a[i][j];
				} 
				//如果是这一行的最后一个数,则与上一行最后一个的最优解相加 
				else if(j==i){
					f[i][j]=f[i-1][j-1]+a[i][j];
				}
				//如果是其他的数,则取两个中最小的那个作为最优解 
				else{
					f[i][j] = min(f[i-1][j-1]+a[i][j],f[i-1][j]+a[i][j]);
				}
			}
		ans = f[n][1]; 
		//在最后一层中找答案(最小值)
		for (int i = 2; i <= n; i++)
			ans = min(ans,f[n][i]);
		printf("%d\n",ans);
	}
}

C. 最长不下降子序列

Description

一个数的序列bi,当b1 <= b2 <= … < =bS的时候,我们称这个序列是不下降的。对于给定的一个序列(a1, a2, …, aN),我们可以得到一些不下降的子序列(ai1, ai2, …, aiK),这里1<= i1 < i2 < … < iK <= N。比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些不下降子序列,如(1, 7), (3, 4, 8)等等。这些子序列中最长的长度是4,比如子序列(1, 3, 5, 8)。

Input

多组cas , 每组cas 两行:

​ 第一行 输入一个数 n (n < 10000), 表示有n个数

​ 第二行 n个数, 分别代表每个数;

Output

每个cas 一行 输出该序列最长不下降子序列的长度 ;

Sample Input

7
1 7 3 5 9 4 8

Sample Output

4

Answer

#include<bits/stdc++.h>
using namespace std;

int a[10001],dp[10001];
int main() {
	int i,j,n,mx;
	while(1==scanf("%d",&n)) {
		for(i=1; i<=n; i++) {
			scanf("%d",&a[i]);
		}
		for(i=1; i<=n; i++) {
			dp[i]=1;
		}
		mx=1;
		for(i=n-1; i>=1; i--) {
			for(j=i+1; j<=n; j++) {
				if(a[i]<=a[j]) {
					dp[i]=max(dp[i],dp[j]+1);
				}
			}
			mx=max(mx,dp[i]);
		}
		printf("%d\n",mx);
	}
	return 0;
}

D. 最长不下降子序列问题(输出序列)

Description

在这里插入图片描述

Input

本问题有多组测试数据,对于每组测试数据,输入有两行;第一行n表示序列的元素个数,1<=n<=10000;第二行是用空格隔开的n个整数,表示由n个整数组成的序列。

Output

对于每一组测试数据,输出有两行,第一行为最长的子序列的长度;第二行是用空格隔开的子序列,行尾没有空格。

Sample Input

7
1 7 3 5 9 4 8
14
13 7 9 16 38 24 37 18 44 19 21 22 63 15

Sample Output

4
1 3 5 9
8
7 9 16 18 19 21 22 63

Answer

#include<bits/stdc++.h>
using namespace std;

int a[10001],dp[10001],b[10001];
int h;
int main() {
	int i,j,n,mx;
	while(1==scanf("%d",&n)) {
		for(i=1; i<=n; i++) {
			scanf("%d",&a[i]);
		}
		for(i=1; i<=n; i++) {
			dp[i]=1;
			b[i]=i;
		}
		mx=1;
		h=1;
		for(i=n-1; i>=1; i--) {
			for(j=i+1; j<=n; j++) {
				if(a[i]<=a[j]&&dp[i]<dp[j]+1) {
					dp[i]=dp[j]+1;
//					数组b 中存了子序列各元素的在a数组的下标 
//					b[i]=j -> b[j]=k ->b[k]
					b[i]=j;
				}
			}
			if(mx<=dp[i]) {
				mx=dp[i];
//				h是子序列第一个数的下标 
				h=i;
			}
		}
		printf("%d\n",mx);
		printf("%d",a[h]);
		for(i=1; i<mx; i++) {
			h=b[h];
			printf(" %d",a[h]);
		}
		printf("\n");
	}
	return 0;
}
  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值