数位DP
例题
求闭区间[l,r]之间有多少个数,用数位DP
定义一个枚举上限x,f[i][j],表示一位一位枚举数字,枚举到了第i位,同时记录这个数是大于还是小于上限x。
f[k+1][1] = 1;
for(int i = k+1;i >= 2; i--){
for(int j = 0;j <= 1; j++){
for(int r = 0;r <= 9; r++){
if(j == 1 && r > y[i-1]) continue;
f[i-1][j && (r == y[i-1])] += f[i][j];
}
}
}
ans = f[1][0] + f[1][1];
P3
求在【L,R】中的满足相邻两个数字之差至少为2的数有多少个
增加一维,记录上一位填的数:f[i][j][k]填到i,j为小于或等于区间,k记录上一位数的情况。
P4
求在[l, r]之间的回文数有多少个.
f[i][j][k]k表示数字的后i为大于/小于/等于x的后i位,k = 0/1/2。
f[i-1][j && (r == y(i-1))][check(r)] +=
P5
求在【L,R】中的满足各位数字之积为K的数有多少个
L,R 10^18
f[i][j][r]从高到低填到i,j表示小于或等于最大值,r表示个位数字之积等于k(r = 0/1)。但是复杂度太高。
f[i][j][a][b][c][d],代表枚举到第i个数,小于/等于,2、3、5、7分别出现了几次。
另特殊处理前导0、非前导0的情况。
f[i-1][j && (r == y[i-1])][a + (r == a)][b + (r == b)][c + (r == c)][d + (r == d)] += f[i][j][a][b][c][d]
现场yy一道题
已知:所有的(x_i in [l,r]),求
的最值
f[i][j][k][p] 表示枚举到i位,大于l,小于r,上一位为p的最值(我也忘了是最大还是最小)。
f[i-1][j && (r == y[i-1])][k && (r == x[i-1])][r] += f[i][j][k][p];
博弈DP
f[i][j][k]放了i行,有j列有0个炮,有k列有1个炮
f[i+1][j][k] += f[i][j][k] // 放0个炮的情况
f[i+1][j-1][k+1] += f[i][j][k] //放了1个炮的个数(本来有0个)
f[i+1][j][k-1] += f[i][j][k] //放了1个炮的个数(本来有1个)
//放两个炮的个数:
f[i+1][j-2][k+2] += f[i][j][k]//两个都放到0上
f[i+1][j][k-2] += f[i][j][k]//两个都放到1上
f[i+1][j-1][k] += f[i][j][k]//一个0一个1
排列DP
有一个1-n的排列,满足某个条件的排列有多少种?
举例子:1-n的排列中,逆序对数量为偶的佩列有多少个。
1-i的排列插入i+1可以得到i+1的排列。
f[i][j]已经把1-i放进排列,形成j个排列的方案数。(也可以把j改成0或1)
f[1][0] = 1;
for(int i = 1;i < n; i++)
for(int j = 0;j <= 1; j++){
for(int k = 0;k <= i; k++){
f[i+1][(j+i-k) % 2] += f[i][j];
}
}
P14
对于一个序列,定义其“激动值”为序列中严格大于前面所有数的元素的个数。比如, {1,1,5,6,5}的激动值为3。 给定n个数p1,p2...pn ,求这n个数的所有排列中,激动值不超过k的个数。1≤k≤n≤200;1≤pi≤200
f[i][j]表示n-i的数全部插入了,此时的激动值为j的排列数。把正着插改成倒着插,问题就解决了。
YY
现在有一个1-n的排列,问最长上升子序列长度不超过2的排列有多少个。
从小到大插,要保证最长上升子序列长度小于等于二。如果此时的子序列等于1或等于2。等于1的话就是插入到最前面。如果把 i+1 插入到序列中,就只能有插入到最前面或者最前面的上升子序列的前面。
f[i][j]插入了1-i个数,最靠前可以插到第j个数。