目录
1、乌龟棋
2、最长上升子序列
3、最长公共子序列
4、松散子序列
5、最大上升子序列和
1、乌龟棋
小明过生日的时候,爸爸送给他一副乌龟棋当作礼物。
乌龟棋的棋盘只有一行,该行有 N 个格子,每个格子上一个分数(非负整数)。
棋盘第 1 格是唯一的起点,第 N 格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点。
乌龟棋中共有 M 张爬行卡片,分成 4 种不同的类型(M 张卡片中不一定包含所有 4 种类型的卡片),每种类型的卡片上分别标有 1、2、3、4 四个数字之一,表示使用这种卡片后,乌龟棋子将向前爬行相应的格子数。
游戏中,玩家每次需要从所有的爬行卡片中选择一张之前没有使用过的爬行卡片,控制乌龟棋子前进相应的格子数,每张卡片只能使用一次。
游戏中,乌龟棋子自动获得起点格子的分数,并且在后续的爬行中每到达一个格子,就得到该格子相应的分数。
玩家最终游戏得分就是乌龟棋子从起点到终点过程中到过的所有格子的分数总和。
很明显,用不同的爬行卡片使用顺序会使得最终游戏的得分不同,小明想要找到一种卡片使用顺序使得最终游戏得分最多。
现在,告诉你棋盘上每个格子的分数和所有的爬行卡片,你能告诉小明,他最多能得到多少分吗?
输入格式
输入文件的每行中两个数之间用一个空格隔开。
第 1 行 2 个正整数 N 和 M,分别表示棋盘格子数和爬行卡片数。
第 2 行 N 个非负整数,a1,a2,……,aN其中 ai表示棋盘第 i 个格子上的分数。
第 3 行 M 个整数,b1,b2,……,bM表示 M 张爬行卡片上的数字。
输入数据保证到达终点时刚好用光 M 张爬行卡片。
输出格式
输出只有 1 行,包含 1 个整数,表示小明最多能得到的分数。
数据范围
1≤N≤350
1≤M≤120
0≤ai≤100
1≤bi≤4
每种爬行卡片的张数不会超过 40。
输入样例:
9 5
6 10 14 2 8 8 18 5 17
1 3 1 2 1
输出样例:
73
思路:
用四个维度表示四个卡片的消耗量,状态转移方程:
auto &v=f[b1][b2][b3][b4];可以减少代码量
int t=a[b1+2*b2+3*b3+4*b4+1];
auto &v=f[b1][b2][b3][b4];
if(b1)v=max(v,f[b1-1][b2][b3][b4]+t);
if(b2)v=max(v,f[b1][b2-1][b3][b4]+t);
if(b3)v=max(v,f[b1][b2][b3-1][b4]+t);
if(b4)v=max(v,f[b1][b2][b3][b4-1]+t);
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=41;
int a[360];
int b[5];
int n,m;
int f[N][N][N][N];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=m;i++)
{
int t;
scanf("%d",&t);
b[t]++;
//cout<<b[t];
}
for(int b1=0;b1<=b[1];b1++)
for(int b2=0;b2<=b[2];b2++)
for(int b3=0;b3<=b[3];b3++)
for(int b4=0;b4<=b[4];b4++)
{
f[b1][b2][b3][b4]=a[b1+2*b2+3*b3+4*b4+1];
}
for(int b1=0;b1<=b[1];b1++)
for(int b2=0;b2<=b[2];b2++)
for(int b3=0;b3<=b[3];b3++)
for(int b4=0;b4<=b[4];b4++)
{
int t=a[b1+2*b2+3*b3+4*b4+1];
auto &v=f[b1][b2][b3][b4];
if(b1)v=max(v,f[b1-1][b2][b3][b4]+t);
if(b2)v=max(v,f[b1][b2-1][b3][b4]+t);
if(b3)v=max(v,f[b1][b2][b3-1][b4]+t);
if(b4)v=max(v,f[b1][b2][b3][b4-1]+t);
}
cout<<f[b[1]][b[2]][b[3]][b[4]];
return 0;
}
2、最长上升子序列
给定一个长度为 N 的数列,求数值严格单调递增的子序列的长度最长是多少。
输入格式
第一行包含整数 N。
第二行包含 N个整数,表示完整序列。
输出格式
输出一个整数,表示最大长度。
数据范围
1≤N≤1000,
−1e9≤数列中的数≤109
输入样例:
7
3 1 2 1 8 5 6
输出样例:
4
思路:
状态转移方程:
if(a[j]<a[i])f[i]=max(f[i],f[j]+1);
res=max(res,f[i]);
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1003;
int a[N];
int n;
int f[N];//以i结尾的最长子序列数
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),f[i]=1;
int res=-1;
for(int i=1;i<=n;i++)
for(int j=0;j<i;j++)
{
if(a[j]<a[i])f[i]=max(f[i],f[j]+1);
res=max(res,f[i]);
}
cout<<res;
return 0;
}
3、最长公共子序列
给定两个长度分别为 N和 M 的字符串 A 和 B,求既是 A 的子序列又是 B 的子序列的字符串长度最长是多少。
输入格式
第一行包含两个整数 N 和 M。
第二行包含一个长度为 N 的字符串,表示字符串 A。
第三行包含一个长度为 M 的字符串,表示字符串 B。
字符串均由小写字母构成。
输出格式
输出一个整数,表示最大长度。
数据范围
1≤N,M≤1000
输入样例:
4 5
acbd
abedc
输出样例:
3
思路:
f[i][j]=max(f[i-1][j],f[i][j-1]);//这两个都算过了
if(a[i]==b[j])f[i][j]=max(f[i][j],f[i-1][j-1]+1);
res=max(res,f[i][j]);
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m;
const int N=1003;
char a[N],b[N];
int f[N][N];
int main()
{
cin>>n>>m;
int res=-1;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=m;i++)cin>>b[i];
//for(int i=1;i<=m;i++)cout<<b[i];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
f[i][j]=max(f[i-1][j],f[i][j-1]);//这两个都算过了
if(a[i]==b[j])f[i][j]=max(f[i][j],f[i-1][j-1]+1);
res=max(res,f[i][j]);
}
cout<<res;
return 0;
}
4、松散子序列
给定一个仅含小写字母的字符串 s,假设 s 的一个子序列 t 的第 i 个字符对应了原字符串中的第 pi 个字符。
我们定义 s 的一个松散子序列为:对于 i>1 总是有 pi − pi−1≥2。
设一个子序列的价值为其包含的每个字符的价值之和(a∼z 分别为 1∼26)。
求 s 的松散子序列中的最大价值。
输入格式
输入一行包含一个字符串 s。
输出格式
输出一行包含一个整数表示答案。
数据范围
对于 20% 的评测用例,|s|≤10;
对于 40% 的评测用例,|s|≤300;
对于 70% 的评测用例,|s|≤5000;
对于所有评测用例,1≤|s|≤1e6,字符串中仅包含小写字母。
输入样例:
azaazaz
输出样例:
78
思路:
状态转移方程:
f[i]=max(f[i],f[i-1]);
if(i-2>=0)f[i]=max(f[i],f[i-2]+(a[i]-'a'+1));
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
char a[N];
int f[N];
int main()
{
scanf("%s",a+1);//从1索引开始读取
int n=strlen(a+1);
for(int i=1;i<=n;i++)
{
//cout<<a[i]<<" ";
f[i]=a[i]-'a'+1;
}
//int res=-1;
for(int i=1;i<=n;i++)
{
f[i]=max(f[i],f[i-1]);
if(i-2>=0)f[i]=max(f[i],f[i-2]+(a[i]-'a'+1));
//cout<<f[i]<<endl;
//res=max(res,f[i]);还可以再优化!
}
cout<<f[n];
return 0;
}
5、最大上升子序列和
一个数的序列 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)等等。
这些子序列中和最大为18,为子序列(1,3,5,9)的和。
你的任务,就是对于给定的序列,求出最大上升子序列和。
注意,最长的上升子序列的和不一定是最大的,比如序列(100,1,2,3)的最大上升子序列和为100,而最长上升子序列为(1,2,3)。
输入格式
输入的第一行是序列的长度N。
第二行给出序列中的N个整数,这些整数的取值范围都在0到10000(可能重复)。
输出格式
输出一个整数,表示最大上升子序列和。
数据范围
1≤N≤1000
输入样例:
7
1 7 3 5 9 4 8
输出样例:
18
思路:
状态转移方程:
if(a[i]>a[j])f[i]=max(f[i],f[j]+a[i]);
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1003;
int a[N];
int f[N];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
f[i]=a[i];
}
int res=-1;
for(int i=1;i<=n;i++)
for(int j=0;j<i;j++)
{
//f[i]=max(f[i],f[j]);错误的,虽然j比i小,a[j]不一定比a[i]小
if(a[i]>a[j])f[i]=max(f[i],f[j]+a[i]);
//cout<<f[i]<<" ";
res=max(res,f[i]);
}
cout<<res;
return 0;
}