第七周题解

这篇博客探讨了动态规划在解决实际问题中的应用,包括采药问题、最长上升子序列、最大子段和以及最长公共子序列。采药问题通过背包问题的思路解决,寻找最大价值;最长上升子序列利用动态规划找到序列中最长的递增子序列;最大子段和通过动态规划找出序列中连续子序列的最大和;最长公共子序列则通过LCS算法求解两个字符串的最长公共子序列。这些例子展示了动态规划在处理复杂优化问题时的有效性。
摘要由CSDN通过智能技术生成

采药

题目描述

辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

如果你是辰辰,你能完成这个任务吗?

输入格式

第一行有 2 2 2 个整数 T T T 1 ≤ T ≤ 1000 1 \le T \le 1000 1T1000)和 M M M 1 ≤ M ≤ 100 1 \le M \le 100 1M100),用一个空格隔开, T T T 代表总共能够用来采药的时间, M M M 代表山洞里的草药的数目。

接下来的 M M M 行每行包括两个在 1 1 1 100 100 100 之间(包括 1 1 1 100 100 100)的整数,分别表示采摘某株草药的时间和这株草药的价值。

输出格式

输出在规定的时间内可以采到的草药的最大总价值。

样例 #1

样例输入 #1

70 3
71 100
69 1
1 2

样例输出 #1

3

提示

【数据范围】

  • 对于 30 % 30\% 30% 的数据, M ≤ 10 M \le 10 M10
  • 对于全部的数据, M ≤ 100 M \le 100 M100

思路

该题类似于背包问题,M颗药草T时间,如果选择放入一颗时间为t的药草,则可以类似M-1课药草,T-t的时间的问题,然后以此类推

代码

#include<iostream>
using namespace std;
int n, m;
int t[101], v[101];
int ves[101][1001];
int main()
{
	cin >> m >> n;
	for (int i = 1;i <= n;i++)
	{
		cin >> t[i] >> v[i];
	}
	for (int i = 1;i <= n;i++)
	{
		for (int j = 0;j <= m;j++)
		{
			if (t[i] > j)
			{
				ves[i][j] = ves[i - 1][j];
			}
			else
			{
				ves[i][j] = ves[i - 1][j] > (ves[i - 1][j - t[i]] + v[i]) ? ves[i - 1][j] : (ves[i - 1][j - t[i]] + v[i]);
			}
		}
	}
	cout << ves[n][m];
	return 0;
}

最长上升子序列

题目描述

这是一个简单的动规板子题。

给出一个由 n ( n ≤ 5000 ) n(n\le 5000) n(n5000) 个不超过 1 0 6 10^6 106 的正整数组成的序列。请输出这个序列的最长上升子序列的长度。

最长上升子序列是指,从原序列中按顺序取出一些数字排在一起,这些数字是逐渐增大的。

输入格式

第一行,一个整数 n n n,表示序列长度。

第二行有 n n n 个整数,表示这个序列。

输出格式

一个整数表示答案。

样例 #1

样例输入 #1

6
1 2 4 1 3 4

样例输出 #1

4

思路

先用数组存储,再开一个等空间的数组存储第n个数之前有多少个数小于自己的然后加一,利用动态规划,如果初始为1,如果第n个数比第n-1个数大则数组值加1,如果小于n-1则找到最近的一个小于他的数,然后加1.

#include<iostream>
#include<algorithm>
using namespace std;
int M[5001];
int main()
{
	int n;
	cin >> n;
	int arr[5001];
	for (int i = 1;i <= n;i++)
	{
		cin >> arr[i];
	}
	M[1] = 1;
	for (int i = 2;i <= n;i++)
	{
		int max = 1;
		for (int j = i - 1;j >= 1;j--)
		{
			if (arr[i] > arr[j])
			{
				max = max > (M[j] + 1) ? max : (M[j] + 1);
			}
		}
		M[i] = max;
	}
	sort(M + 1, M + n + 1);
	cout << M[n];
	return 0;
}

最大子段和

题目描述

给出一个长度为 n n n 的序列 a a a,选出其中连续且非空的一段使得这段和最大。

输入格式

第一行是一个整数,表示序列的长度 n n n

第二行有 n n n 个整数,第 i i i 个整数表示序列的第 i i i 个数字 a i a_i ai

输出格式

输出一行一个整数表示答案。

样例 #1

样例输入 #1

7
2 -4 3 -1 2 -4 3

样例输出 #1

4

提示

样例 1 解释

选取 [ 3 , 5 ] [3, 5] [3,5] 子段 { 3 , − 1 , 2 } \{3, -1, 2\} {3,1,2},其和为 4 4 4

思路

用数组存储前n个数最大的和,如果前n-1的和大于0则数组n的值为第n个数的值加上前n-1个数的最大和。

代码

#include<iostream>
#include<algorithm>
using namespace std;
int n;
int arr[200000];
int m[200000];
int main()
{
	cin >> n;
	for (int i = 0;i < n;i++)
	{
		cin >> arr[i];
	}
	m[0] = arr[0];
	for (int i = 1;i < n;i++)
	{
		m[i] = m[i - 1] > 0 ? (m[i - 1] + arr[i]) : arr[i];
	}
	sort(m, m + n);
	cout << m[n - 1];
	return 0;
}

LCS

题面翻译

题目描述:

给定一个字符串 s s s 和一个字符串 t t t ,输出 s s s t t t 的最长公共子序列。

输入格式:

两行,第一行输入 s s s ,第二行输入 t t t

输出格式:

输出 s s s t t t 的最长公共子序列。如果有多种答案,输出任何一个都可以。

说明/提示:

数据保证 s s s t t t 仅含英文小写字母,并且 s s s t t t 的长度小于等于3000。

题目描述

文字列 $ s $ および $ t $ が与えられます。 $ s $ の部分列かつ $ t $ の部分列であるような文字列のうち、最長のものをひとつ求めてください。

输入格式

入力は以下の形式で標準入力から与えられる。

$ s $ $ t $

输出格式

$ s $ の部分列かつ $ t $ の部分列であるような文字列のうち、最長のものをひとつ出力せよ。 答えが複数ある場合、どれを出力してもよい。

样例 #1

样例输入 #1

axyb
abyxb

样例输出 #1

axb

样例 #2

样例输入 #2

aa
xayaz

样例输出 #2

aa

样例 #3

样例输入 #3

a
z

样例输出 #3


样例 #4

样例输入 #4

abracadabra
avadakedavra

样例输出 #4

aaadara

思路

利用LSC算法先求出对应二维数组,再逆序遍历找到相应的值

代码

#include<iostream>
#include<string>
#include<stack>
using namespace std;
string s1;
string s2;
int arr[3001][3001];
int c[3001][3001];
int max(int x1, int y1, int x2,int y2)
{
	if (arr[x1][y1] >= arr[x2][y2])
	{
		return arr[x1][y1];
	}
	else
	{
		return arr[x2][y2];
	}
}
void LCS(int i, int j)
{
	if (i == 0 || j == 0)
	{
		return;
	}
	else
	{
		if (s1[i - 1] == s2[j - 1])
		{
			LCS(i - 1, j - 1);
			cout << s1[i - 1];
		}
		else if(arr[i][j-1]>arr[i-1][j])
		{
			LCS(i, j - 1);
		}
		else
		{
			LCS(i - 1, j);
		}
	}
}
int main()
{
	cin >> s1 >> s2;
	for (int i = 0;i < s1.length();i++)
	{
		for (int j = 0;j < s2.length();j++)
		{
			if (s1[i] == s2[j])
			{
				arr[i + 1][j + 1] = arr[i][j] + 1;
			}
			else
			{
				arr[i + 1][j + 1] = max(i + 1, j, i, j + 1);
			}
		}
	}
	LCS(s1.length(), s2.length());
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值