度小满2024春招算法方向第1批:阶梯(最长递增子序列)、北京天坛,涉及动态规划、数学、等差数列等知识点。
阶梯
题目描述
你有 n n n 个箱子,它们的高度分别为 a i a_i ai,你想要用它们做出一个尽可能长的阶梯。但是你对最长的阶梯长度不感兴趣,你只对其方案数感兴趣。 形式化地,给出长度为 n n n 的序列 a i {ai} ai,从中挑选出子序列 b i {bi} bi,满足对所有合法下标 i i i,有 b i < b i + 1 b_i<b_i+1 bi<bi+1 成立(即单调递增,如果子序列 b i {b_i} bi 长度为 1 1 1,亦视为满足此条件)。
在这些子序列中,长度为最大长度的子序列有多少个?
子序列:某个序列的子序列是从最初序列通过去除某些元素但不破坏余下元素的相对位置(在前或在后)而形成的新序列。
我们认为两个子序列相同,当且仅当所有数都是从相同位置选出来的。而对于序列 1 , 2 , 2 , 6 {1,2,2,6} 1,2,2,6,选择第 2 2 2 个和第 4 4 4 个形成子序列 2 , 6 {2,6} 2,6,选择第 3 3 3 个和第 4 4 4 个形成子序列 2 , 6 {2,6} 2,6,虽然形式相同但仍视为不同的序列。
输入描述
第一行一个正整数 n n n 表示序列长度。
第二行 n n n 个由空格隔开的正整数 a i a_i ai ,依次表示 a 1 a_1 a1 到 a n a_n an。
对于 100 100% 100 的数据, 1 ≤ n ≤ 3000 1≤n≤3000 1≤n≤3000, a i ≤ 1 0 9 a_i≤10^9 ai≤109。
输出描述
一行一个数,表示答案,对 1 0 9 + 7 10^9+7 109+7 取模。
解题思路
最长递增子序列问题的变形。
令 d p [ i ] dp[i] dp[i] = 以 a i a_i ai 结尾的最长递增子序列的长度,则 d p [ i ] = m a x ( d p [ 1 ] , d p [ 2 ] , . . . , d p [ i − 1 ] ) dp[i] = max(dp[1], dp[2], ..., dp[i-1]) dp[i]=max(dp[1],dp[2],...,dp[i−1])。
令 c n t [ i ] cnt[i] cnt[i] = 以 a i a_i ai 结尾的最长递增子序列的数量,则对于 j < i j<i j<i,若 d p [ i ] < d p [ j ] + 1 dp[i]<dp[j]+1 dp[i]<dp[j]+1,则 c n t [ i ] = c n t [ j ] cnt[i]=cnt[j] cnt[i]=cnt[j],若 d p [ i ] = d p [ j ] + 1 dp[i]=dp[j]+1 dp[i]=dp[j]+1,则 c n t [ i ] + = c n t [ j ] cnt[i]+=cnt[j] cnt[i]+=cnt[j]。
代码实现
typedef long long ll;
const int N = 3e3 + 5, MOD = 1e9 + 7;
int dp[N], cnt[N], a[N];
int main() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
int ml = 0;
for (int i = 1; i <= n; i++) {
// dp和cnt的初始值
dp[i] = cnt[i] = 1;
for (int j = 1; j < i; j++)
if (a[j] < a[i]) {
if (dp[i] < dp[j] + 1)
dp[i] = dp[j] + 1, cnt[i] = cnt[j];
else if (dp[i] == dp[j] + 1)
cnt[i] += cnt[j];
}
// 记录最长递增子序列的长度
ml = max(ml, dp[i]);
}
// 累计所有长度为ml的最长递增子序列的长度
ll res = 0;
for (int i = 1; i <= n; i++)
if (dp[i] == ml)res = (res + cnt[i]) % MOD;
printf("%lld", res);
return 0;
}
时间复杂度: O ( n 2 ) O(n^2) O(n2)
空间复杂度: O ( n ) O(n) O(n)
北京天坛
题目描述
北京天坛的圜丘坛为古代祭天的场所,分上、中、下三层,上层中心有一块圆形石板(称为天心石),环绕天心石砌 m m m 块扇面形石板构成第一环,向外每环依次增加 m m m 块。下一层的第一环比上一层的最后一环多 m m m 块,向外每环依次增加 m m m 块。
已知每层环数相同。现给出每层的环数 n n n 和每一环比上一环增加的块数为 m m m,求总共有多少块扇面形石板?
输入描述
单行输入。
两个正整数 n n n 和 m m m,表示每层的环数和每一环比上一环增加的块数( n < 1 e 5 , m < 1 e 5 n<1e5,m<1e5 n<1e5,m<1e5),两个正整数之间用空格隔开。
输出描述
输出扇面形石板的总数。
解题思路
不难发现,这是一道等差数列求和题,首项为 m m m,公差为 3 × n 3×n 3×n。
代码实现
int main() {
long long n, m;
cin >> n >> m;
cout << (1 + 3 * n) * 3 * m * n / 2;
return 0;
}
时间复杂度: O ( 1 ) O(1) O(1)。
空间复杂度: O ( 1 ) O(1) O(1)。
END
文章文档:公众号 字节幺零二四
回复关键字可获取本文文档。
文章声明:题目来源 牛客 平台,如有侵权,请联系删除!