2020/4/22
1.组合数学dp 传送门
定义一个数组为good,当且仅当它能够划分为若干个这样的子段(首元素等于区间长度-1 且 首元素>0)。求给出序列(长度1e3)中有多少个这样的子序列%mod。
可能有点绕 上一下样例。
输入
4
1 1 1 1
输出
7
包括任取两个,因为数组[1,1]中a[1]=区间长度-1=2-1=1
以及全选,因为1 1 1 1可以划分为两个数组[1,1]
很自然就想到在i处时可以再其后任取a[i]个(a[i]>0时),但是这只是单独一个的,很明显两个成立的子序列拼接起来依旧成立,在这里就想断了。
看了题解是一个dp。
dp[j]表示位置j到结尾成立的序列数,那么dp[i]=ΣC(j-i-1,a[i]-1)*(1+dp[j+1]),再加上dp[i+1]。即在位置i处到位置j处(确定首尾),再在区间中任取a[i]-1个,再拼接上dp[j+1]的答案,+1是因为可以不拼接。
这里组合数用的比较频繁,采取递推预处理的方式。上代码。
#include
using namespace std;
typedef long long ll;
typedef pair pii;
typedef pair pll;
const int maxn = 1e3 + 5;
const ll inf = 0x3f3f3f3f;
const ll mod = 998244353;
#define all(x) x.begin(), x.end()
int n;
ll a[maxn], dp[maxn], C[maxn][maxn];
int main() {
for (int i = 0;
i <= 1000;
i++) C[i][0] = 1, C[i][1] = i, C[i][i] = 1;
for (int i = 1;
i <= 1000;
i++) {
for (int j = 1;
j <= 1000;
j++) {
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
}
}
scanf("%d", &n);
for (int i = 0;
i < n;
++i) scanf("%lld", a + i);
for (int i = n - 1;
i >= 0;
--i) {
dp[i] += dp[i + 1];
if (a[i] > 0) {
for (int j = i + a[i];
j < n;
++j) {
dp[i] += C[j - i - 1][a[i] - 1] * (dp[j + 1] + 1) % mod;
dp[i] %= mod;
}
}
}
printf("%lld\n", dp[0]);
return 0;
}
2.字符串dp 传送门
题意就是两个字符串中选出k个不重叠的公共子串的最长长度和。
输入
9 12 4
bbaaababb
abbbabbaaaba
输出
7
【#|2020/4/22每日一练】
文章图片
emmm,完全没有思路。dp思想属实不够。
题解是这样的dp,dp[i][j][k][0/1]表示s串到位置i,t串中到位置j时,已经有k个相同的子串,最后一位表示当前结尾是否为最后一个子串的结尾(用于判断能否接上)。有了这个定义应该就很容易转移了。
搬一下别人的题解吧,我就不打了。
别人的题解
推荐阅读
- 人工智能|干货!人体姿态估计与运动预测
- 分析COMP122 The Caesar Cipher
- 技术|为参加2021年蓝桥杯Java软件开发大学B组细心整理常见基础知识、搜索和常用算法解析例题(持续更新...)
- C语言学习(bit)|16.C语言进阶——深度剖析数据在内存中的存储
- Python机器学习基础与进阶|Python机器学习--集成学习算法--XGBoost算法
- 数据结构与算法|【算法】力扣第 266场周赛
- 数据结构和算法|LeetCode 的正确使用方式
- leetcode|今天开始记录自己的力扣之路
- 人工智能|【机器学习】深度盘点(详细介绍 Python 中的 7 种交叉验证方法!)