# [NOIP2005 普及组] 采药
## 题目描述
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是辰辰,你能完成这个任务吗?
## 输入格式
第一行有 $2$ 个整数 $T$($1 \le T \le 1000$)和 $M$($1 \le M \le 100$),用一个空格隔开,$T$ 代表总共能够用来采药的时间,$M$ 代表山洞里的草药的数目。
接下来的 $M$ 行每行包括两个在 $1$ 到 $100$ 之间(包括 $1$ 和 $100$)的整数,分别表示采摘某株草药的时间和这株草药的价值。
## 输出格式
输出在规定的时间内可以采到的草药的最大总价值。
## 样例 #1
### 样例输入 #1
```
70 3
71 100
69 1
1 2
```
### 样例输出 #1
```
3
```
## 提示
**【数据范围】**
- 对于 $30\%$ 的数据,$M \le 10$;
- 对于全部的数据,$M \le 100$。
**【题目来源】**
NOIP 2005 普及组第三题
本题考查dp的基础 ,记忆化搜索与遍历,以此节省时间。代码如下
#include<bits/stdc++.h>
using namespace std;
int yao[101][2],ans,vis[101][1001];
int dfs(int n,int w)
{
if(n==0)ans=0;
else if(vis[n][w]!=-1) ans=vis[n][w];
else if(w<yao[n][0]) ans=dfs(n-1,w);
else
{
ans=max(dfs(n-1,w),dfs(n-1,w-yao[n][0])+yao[n][1]);
}
vis[n][w]=ans;
return ans;
}
int main()
{
memset(vis,-1,sizeof(vis));
int t,m;
cin>>t>>m;
for(int i=1;i<=m;i++)
{
cin>>yao[i][0]>>yao[i][1];
}
cout<<dfs(m,t);
return 0;
}
# 最长上升子序列
## 题目描述
这是一个简单的动规板子题。
给出一个由 $n(n\le 5000)$ 个不超过 $10^6$ 的正整数组成的序列。请输出这个序列的**最长上升子序列**的长度。
最长上升子序列是指,从原序列中**按顺序**取出一些数字排在一起,这些数字是**逐渐增大**的。
## 输入格式
第一行,一个整数 $n$,表示序列长度。
第二行有 $n$ 个整数,表示这个序列。
## 输出格式
一个整数表示答案。
## 样例 #1
### 样例输入 #1
```
6
1 2 4 1 3 4
```
### 样例输出 #1
```
4
```
## 提示
分别取出 $1$、$2$、$3$、$4$ 即可。
同样是记忆化搜索,不过 需要把上一个采用的点记录下来。代码如下
#include<bits/stdc++.h>
using namespace std;
int vis[5005][5005],a[5005];
int ans;
int dfs(int now,int pre)
{
if(now<1)return 0;
else if(vis[now][pre]!=-1) ans=vis[now][pre];
else if(a[now]<a[pre]) ans=max(dfs(now-1,now)+1,dfs(now-1,pre));
else ans=dfs(now-1,pre);
vis[now][pre]=ans;
return ans;
}
int main()
{
memset(vis,-1,sizeof(vis));
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
a[n+1]=1000001;
cout<<dfs(n+1,n+1);
return 0;
}
# 最大子段和
## 题目描述
给出一个长度为 $n$ 的序列 $a$,选出其中连续且非空的一段使得这段和最大。
## 输入格式
第一行是一个整数,表示序列的长度 $n$。
第二行有 $n$ 个整数,第 $i$ 个整数表示序列的第 $i$ 个数字 $a_i$。
## 输出格式
输出一行一个整数表示答案。
## 样例 #1
### 样例输入 #1
```
7
2 -4 3 -1 2 -4 3
```
### 样例输出 #1
```
4
```
## 提示
#### 样例 1 解释
选取 $[3, 5]$ 子段 $\{3, -1, 2\}$,其和为 $4$。
#### 数据规模与约定
- 对于 $40\%$ 的数据,保证 $n \leq 2 \times 10^3$。
- 对于 $100\%$ 的数据,保证 $1 \leq n \leq 2 \times 10^5$,$-10^4 \leq a_i \leq 10^4$。
本题只要逐个采用,如果小于零就索性清零,每次记录较大值即可。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,sum,Max,cur;
cin>>n>>Max;
sum=Max;
while(--n)
{
cin>>cur;
sum=sum>0?sum:0;
sum+=cur;
Max=Max>sum?Max:sum;
}
cout<<Max;
return 0;
}
# LCS
## 题面翻译
题目描述:
给定一个字符串 $s$ 和一个字符串 $t$ ,输出 $s$ 和 $t$ 的最长公共子序列。
输入格式:
两行,第一行输入 $s$ ,第二行输入 $t$ 。
输出格式:
输出 $s$ 和 $t$ 的最长公共子序列。如果有多种答案,输出任何一个都可以。
说明/提示:
数据保证 $s$ 和 $t$ 仅含英文小写字母,并且 $s$ 和 $t$ 的长度小于等于3000。
## 题目描述
[problemUrl]: https://atcoder.jp/contests/dp/tasks/dp_f
文字列 $ 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
答えは `` (空文字列) です。
一道LCS题,难点在于发现不同段之间字段的联系以及记录答案,实现记忆化搜索,利用递归求解即可;代码如下
#include<bits/stdc++.h>
using namespace std;
char s[3005],t[3005],ans[3005];
int dp[3005][3005],vis[3005][3005];
int fill(int a,int b)
{
if(a<0 || b<0)return 0;
else if(dp[a][b])return dp[a][b];
else if(s[a]==t[b])
{
vis[a][b]=1;
return dp[a][b]=fill(a-1,b-1)+1;
}
else
{
if(fill(a-1,b)>fill(a,b-1))
{
vis[a][b]=2;
return dp[a][b]=fill(a-1,b);
}
else
{
vis[a][b]=3;
return dp[a][b]=fill(a,b-1);
}
}
}
void sc(int a,int b)
{
if(a<0||b<0) return;
if(vis[a][b]==1) sc(a-1,b-1),cout<<s[a];
else if(vis[a][b]==2)sc(a-1,b);
else sc(a,b-1);
}
int main()
{
memset(dp,0,sizeof(dp));
memset(vis,0,sizeof(vis));
cin>>s>>t;
int m1,m2;
m1=strlen(s),m2=strlen(t);
m1--,m2--;
fill(m1,m2);
sc(m1,m2);
return 0;
}