[NOIP2005 普及组] 采药
题目描述
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是辰辰,你能完成这个任务吗?
输入格式
第一行有 2 2 2 个整数 T T T( 1 ≤ T ≤ 1000 1 \le T \le 1000 1≤T≤1000)和 M M M( 1 ≤ M ≤ 100 1 \le M \le 100 1≤M≤100),用一个空格隔开, 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 M≤10;
- 对于全部的数据, M ≤ 100 M \le 100 M≤100。
【题目来源】
NOIP 2005 普及组第三题
这是一个背包问题。
具体地,给定一些物品,每个物品有一个体积和一个价值,要将这些物品装到容积为 t 的背包里,使得背包内的物品价值最大。
具体实现如下:
#include<iostream>
using namespace std;
const int N=1005;
int T[N],V[N],val[N][N];
int main(){
int t,m;
cin>>t>>m;
for(int i=1;i<=m;i++){
cin>>T[i]>>V[i];
for(int j=1;j<=t;j++){
val[i][j]=val[i-1][j];
if(j>=T[i]){
val[i][j]=max(val[i][j],val[i-1][j-T[i]]+V[i]);
}
}
}
cout<<val[m][t];
}
输入的第一个数 t 表示背包的容积,第二个数 m 表示物品的数量。接下来的 m 行,每行包含一个物品的体积和价值,分别存储在数组 T 和 V 中。
主函数中,程序通过一个循环来逐一处理每一个物品。对于第 i 个物品,程序使用一个嵌套循环来枚举背包的容积 j。val[i] [j] 表示将前 i 个物品装到容积为 j 的背包中所能获得的最大价值。程序先将 val[i] [j] 赋值为 val[i-1] [j],表示不选择第 i 个物品的情况。然后,如果容积 j 大于等于物品 i 的体积,程序再将 val[i] [j] 更新为 max(val[i] [j], val[[i-1] ] [j-T[i] ]+V[i]),表示选择第 i 个物品的情况。最后,程序输出 val[m] [t],即最终的答案。
最长上升子序列
题目描述
这是一个简单的动规板子题。
给出一个由 n ( n ≤ 5000 ) n(n\le 5000) n(n≤5000) 个不超过 1 0 6 10^6 106 的正整数组成的序列。请输出这个序列的最长上升子序列的长度。
最长上升子序列是指,从原序列中按顺序取出一些数字排在一起,这些数字是逐渐增大的。
输入格式
第一行,一个整数 n n n,表示序列长度。
第二行有 n n n 个整数,表示这个序列。
输出格式
一个整数表示答案。
样例 #1
样例输入 #1
6
1 2 4 1 3 4
样例输出 #1
4
提示
分别取出 1 1 1、 2 2 2、 3 3 3、 4 4 4 即可。
#include <iostream>
using namespace std;
int N, a[100005], f[100005];
int main()
{
cin >> N;
for (int i = 1; i <= N; ++i)
{
cin >> a[i];
}
f[1] = 1;
int ans = 1;
for (int i = 2; i <= N; ++i)
{
f[i] = 1;
for (int j = 1; j < i; j++)
{
if (a[j] < a[i])
{
if (f[j] + 1 > f[i])
{
f[i] = f[j] + 1;
}
}
}
if (f[i] > ans)
ans = f[i];
}
cout << ans;
return 0;
}
输入的第一个数 N 表示序列 a 的长度。接下来的 N 行,每行包含一个整数,表示序列 a 中的元素。数组 f[i] 表示以 a[i] 结尾的最长不下降子序列的长度。
主函数中,程序首先将 f[1] 赋值为 1。然后,通过一个循环来处理每一个元素 a[i]。对于每一个元素 a[i],程序使用另一个循环来枚举之前的元素 a[j]。如果 a[j] < a[i],说明 a[j] 可以作为 a[i] 所在的最长不下降子序列的一部分,因此程序将 f[i] 更新为 max(f[i], f[j] + 1)。最后,程序输出 f[1] 到 f[N] 中的最大值,即为最终的答案。
最大子段和
题目描述
给出一个长度为 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。
数据规模与约定
- 对于 40 % 40\% 40% 的数据,保证 n ≤ 2 × 1 0 3 n \leq 2 \times 10^3 n≤2×103。
- 对于 100 % 100\% 100% 的数据,保证 1 ≤ n ≤ 2 × 1 0 5 1 \leq n \leq 2 \times 10^5 1≤n≤2×105, − 1 0 4 ≤ a i ≤ 1 0 4 -10^4 \leq a_i \leq 10^4 −104≤ai≤104。
#include <bits/stdc++.h>
using namespace std;
int a[1];
int num;
long long maxn = -1e5;
long long sum;
int main()
{
int n;
cin >> n;
cin >> a[0];
sum = a[0];
maxn=a[0];
while (--n)
{
cin >> a[0];
sum=max(sum,(long long) (0));
sum += a[0];
maxn = max(sum, maxn);
}
cout << maxn;
}
具体地,给定一个数组 a,要找到一个子段,使得子段中所有元素的和最大。
输入的第一个数 n 表示数组 a 的长度。接下来的 n 行,每行包含一个整数,表示数组 a 中的元素。变量 maxn 用来存储最大子段和,变量 sum 用来存储当前子段的和。
在主函数中,程序首先读入第一个元素 a[0],并将 sum 赋值为 a[0],maxn 赋值为 a[0]。然后,通过一个循环来处理剩余的 n-1 个元素。对于每一个元素 a[0],程序先将 sum 更新为 max(sum, 0),表示如果当前子段和为负数,则舍弃;然后再将 sum 加上 a[0],表示将 a[0] 加入当前子段。最后,程序更新 maxn 的值,使其等于 max(sum, maxn)。最后,程序输出 maxn,即为最终的答案。
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
提示
注釈
文字列 $ x $ の部分列とは、$ x $ から $ 0 $ 個以上の文字を取り除いた後、残りの文字を元の順序で連結して得られる文字列のことです。
制約
- $ s $ および $ t $ は英小文字からなる文字列である。
- $ 1\ \leq\ |s|,\ |t|\ \leq\ 3000 $
Sample Explanation 1
答えは axb
または ayb
です。 どちらを出力しても正解となります。
Sample Explanation 3
答えは `` (空文字列) です。
#include <iostream>
#include <cstring>
#include<algorithm>
using namespace std;
const int N = 3010;
char s[N], t[N];
int dp[N][N];
int main()
{
cin >> (s + 1) >> (t + 1);
int m = strlen(s + 1), n = strlen(t + 1);
for (int i = 1; i <= m; i++)
for (int j = 1; j <= n; j++)
{
if (s[i] == t[j])
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
int i = m, j = n;
string ans;
while (i && j)
{
if (s[i] == t[j])
ans += s[i], i--, j--;
else if (dp[i][j] == dp[i - 1][j])
i--;
else
j--;
}
reverse(ans.begin(), ans.end());
cout << ans;
return 0;
}
这是一道求解最长公共子序列 (LCS) 的问题。
LCS 问题是在两个字符串中找到一个最长的子序列,使得该子序列在两个字符串中出现。
解决 LCS 问题的常用方法是使用动态规划。我们可以定义一个二维数组 dp,其中 dp[i][j] 表示字符串 ss 的前 i 个字符和字符串 tt 的前 j 个字符的 LCS 长度。
如果 ss[i]==tt[j],则 dp[i] [j]=dp[i-1] [j-1]+1,否则 dp[i] [j]=max(dp[i-1] [j],dp[i] [j-1])。
最终,LCS 的长度即为 dp[len(ss)] [len(tt)]。我们还可以使用回溯的方法找到 LCS 序列本身。