dp合集
- 线性dp
想到了排序a,以及背包,但是看了下数据范围以为不可以背包,但是可以发现 $\sum_{i \in S} \space b_i$ 不会大于5000,所以可以背包
不能只开一维dp数组,设置为一维会导致很多个第 i 位位置状态叠在一起
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
#define int long long
typedef long long ll;
const int N = 5e3 + 5;
const int mod = 998244353;
struct Node{
int x, y;
}a[N];
bool cmp(Node t1, Node t2){
return t1.x < t2.x;
}
//dp数组不能设置为一维,设置为一维会导致很多个第i位位置状态叠在一起
int f[N][N][2];
signed main(){
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int n; cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i].x;
for(int i = 1; i <= n; i++) cin >> a[i].y;
sort(a + 1, a + 1 + n, cmp);
int ans = 0; f[0][0][0] = 1;
for(int i = 1; i <= n; i++) {
int val = a[i].y;
for(int j = 0; j <= 5000; j++) {
f[i][j][0] = (f[i - 1][j][0] + f[i - 1][j][1]) % mod;
if(j - val >= 0)f[i][j][1] = (f[i - 1][j - val][0] + f[i - 1][j - val][1]) % mod;
}
}
for(int i = 1; i <= n; i++) {
int val = a[i].y, maxn = a[i].x;
for(int j = val; j <= maxn; j++) {
ans = (ans + f[i][j][1]) % mod;
}
}
cout << ans << endl;
return 0;
}
对于$\forall k \in [1,n]$,求出把 [1,n] 中的 n 个整数分为非空的 k 组, 每组任意两个数模 m 不同余的方案数。
两个方案不同,当且仅当存在两个数,一种方案中它们在同一组, 但在另一种方案中,它们不同组。
对 998244353 取模。
$2 \le M \le N \le 5000$
$dp_{i, j}$ 维护前$\space i\space$个数,现在已经开了$\space j\space$组
那么开新的一组转移方程就是$\space dp_{i,j} \space =\space dp_{i-1, j-1}$
如果是加入原来开好的组,那么以$\space x\space$表示到$\space i\space$前有多少组已经被用了,那么转移方程就是$dp_{i,j}\space=\space dp_{i-1,j}\space \times \space x$
#include<bits/stdc++.h> using namespace std; #define endl "\n" typedef long long ll; const int N = 5005; const int mod = 998244353; //第 i 个数,当前有 j 组 ll f[N][N]; int main(){ ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); int n, m; cin >> n >> m; f[0][0] = 1; for(int i = 1; i <= n; i++){ for(int j = 1; j <= i; j++){ //开一组新的 f[i][j] = (f[i][j] + f[i - 1][j - 1]) % mod; //加到原来开出来的组中 f[i][j] = (f[i][j] + f[i - 1][j] * max((ll)(j - (i - 1) / m), 0ll)) % mod; } } for(int i = 1; i <= n; i++) cout << f[n][i] << endl; return 0; }