1.最大子段和
Description
给出一个长度为 n 的序列 a,选出其中连续且非空的一段使得这段和最大。
Input
第一行是一个整数,表示序列的长度 n。
第二行有 n 个整数,第 i 个整数表示序列的第 i个数字 ai。
Output
输出一行一个整数表示答案。
数据规模与约定
- 对于 40%40% 的数据,保证 n≤2×103n≤2×103。
- 对于 100%100% 的数据,保证 1≤n≤2×1051≤n≤2×105,−104≤ai≤104−104≤ai≤104。
思路:首先我们看到数据给我们的范围非常大,所以常规的算出所有段的大小并比较肯定超时,那么这时候就要用到动态规划的思想,本题要求我们在给定序列里选择一段连续且和最大的子序列,我们可以遍历整个数组,将遍历到的值不断累加,如果累加后的值小于正在遍历的值,可以明显直到前面累加的值小于0,对最大和只有负影响,所以我们应该把前面累加值去除。最后遍历完成,得到最大和。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a,n,b,ans=-52422452;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a;
if(i==1)b=a;
else
{
b=max(a,b+a);
}
ans=max(ans,b);
}
cout<<ans;
}
2.采药
Description
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是辰辰,你能完成这个任务吗?
Input
第一行有 2个整数 T(1≤T≤1000)和 MM(1≤M≤100),用一个空格隔开,T 代表总共能够用来采药的时间,MM 代表山洞里的草药的数目。
接下来的 MM 行每行包括两个在 1到100 之间(包括 1 和 100)的整数,分别表示采摘某株草药的时间和这株草药的价值。
Output
输出在规定的时间内可以采到的草药的最大总价值。
思路:
-
问题分析:我们需要在有限的时间内选择草药,使得它们的总价值最大。每种草药只能采摘一次,因此这是一个0-1背包问题。
-
动态规划解法:使用一个一维数组
dp
来记录不同时间点下的最大价值。dp[j]
表示在时间j
内可以采到的草药的最大总价值。 -
状态转移:对于每种草药,我们从时间
T
倒序遍历到该草药的时间t
,更新每个时间点的最大价值。具体来说,dp[j] = max(dp[j], dp[j - t] + v)
,其中t
是当前草药的时间,v
是其价值。 -
初始化:数组
dp
初始化为0,表示没有任何草药时所有时间点的价值都是0。 -
外层循环遍历草药:逐个考虑每个草药。
-
内层循环逆序遍历时间:从
T
到t
,保证每个草药只被选择一次。 -
为什么逆序?逆序可避免同一草药被多次选择,确保每个草药仅被处理一次。
解决代码
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
int T,M;
cin>>T>>M;
int dp[1001]={0};
for(int i=0;i<M;i++)
{
int t,v;
cin>>t>>v;
for(int j=T;j>=t;j--)
{
dp[j]=max(dp[j],dp[j-t]+v);
}
}
cout<<dp[T];
return 0;
}
3.宝物筛选
Description
终于,破解了千年的难题。小 FF 找到了王室的宝物室,里面堆满了无数价值连城的宝物。
这下小 FF 可发财了,嘎嘎。但是这里的宝物实在是太多了,小 FF 的采集车似乎装不下那么多宝物。看来小 FF 只能含泪舍弃其中的一部分宝物了。
小 FF 对洞穴里的宝物进行了整理,他发现每样宝物都有一件或者多件。他粗略估算了下每样宝物的价值,之后开始了宝物筛选工作:小 FF 有一个最大载重为 W 的采集车,洞穴里总共有 n 种宝物,每种宝物的价值为 vi,重量为 wi,每种宝物有 mi 件。小 FF 希望在采集车不超载的前提下,选择一些宝物装进采集车,使得它们的价值和最大。
Input
第一行为一个整数 n 和 W,分别表示宝物种数和采集车的最大载重。
接下来 n 行每行三个整数 vi,wi,mi。
Output
输出仅一个整数,表示在采集车不超载的情况下收集的宝物的最大价值。
思路:本题与第二题很像,但是比第二题多了一个条件就是每种宝物不止一件,那么我们该如何处理呢,很自然的我们想到可以将每一件宝物看做是单独的一种,就和第二题是同种问题了,但是由于数据范围较大,那我们必须对代码进行优化,我们可以采用二进制优化的方式,详情查看下方链接二进制优化
#include <iostream>
using namespace std;
const int N = 1e6 + 10;
int n, W, cnt;
int v[N], w[N];
int dp[N];
int main()
{
cin >> n >> W;
for(int i=1; i<=n; i++)
{
int x, y, z;
cin >> x >> y >> z;
for(int j=1; j<=z; j<<=1)
{
v[++cnt] = x * j;
w[cnt] = y * j;
z -= j;
}
if(z)
{
v[++cnt] = x * z;
w[cnt] = y * z;
}
}
for(int i=1; i<=cnt; i++)
{
for(int j=W; j>=w[i]; j--)
{
dp[j] = max(dp[j], dp[j-w[i]]+v[i]);
}
}
cout << dp[W];
return 0;
}
4.最长公共子序列
Description
给出 1,2,…,n 的两个排列 P1 和 P2 ,求它们的最长公共子序列。
Input
第一行是一个数 n。
接下来两行,每行为 n 个数,为自然数 1,2,…,n的一个排列。
Output
一个数,即最长公共子序列的长度。
思路:首先通过观察题目可知上下两个排列有相同元素构成只有顺序不同‘
我们可以以第一个串为标准,用第二个串来匹配第一个串,看能匹配多少,所以,其实第一个串的每个数字其实影响不大,只有知道它对应了第二串的哪个数字就好了,那么我们为什么不把他给的串重新定义一下?
比如他的样例:3 2 1 4 5 我们把他变成 1 2 3 4 5 用一个数组记录一下每个数字变成了什么,相当于离散化了一下3-1;2-2;1-3;4-4;5-5;
现在我们的第二串1 2 3 4 5 按我们离散化的表示:3 2 1 4 5
那么此时怎么找公共子序列呢,我们可以发现第一串进过离散化处理后已经变为单调不减的序列,又两个队列拥有的元素一样,那么此时如果从第二个队列中得到的子序列保存单调不减那么是否就是公共的呢?没错,现在的问题就变成了求一个最长不下降序列!代码如下:
#include<bits/stdc++.h>
using namespace std;
int a1[100010],a2[100010];
int belong[100010];
int b[100010],len;
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a1[i];
belong[a1[i]]=i;
}
for(int i=1;i<=n;i++)
{
cin>>a2[i];
}
for(int i=1;i<=n;i++)
{
if(belong[a2[i]]>b[len])
{
b[++len]=belong[a2[i]];
continue;
}
int k=lower_bound(b+1,b+len+1,belong[a2[i]])-b;
b[k]=belong[a2[i]];
}
cout<<len;
return 0;
}