HDU 4352 区间的有多少个数字满足数字的每一位上的数组成的最长递增子序列为K(数位DP+LIS)
题目:区间的有多少个数字满足数字的每一位上的数组成的最长递增子序列为K
思路:用dp[i][state][j]表示到第i位状态为state,最长上升序列的长度为k的方案数。那么只要模拟nlogn写法的最长上升子序列的求法就行了。这里这里记忆化的时候一定要写成dp[pos][stata][k],表示前pos位,状态为state的,最长上升子序列长为k的方案数这里如果写成dp[pos][state][len]时会出错,因为有多组样例,每一组的k的值不同,那么不同k下得出的dp[pos][state][len]所对应的意义也不同。
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long LL; LL L,R,K,dp[20][1<<10][12],bit[20]; int sum(int x){ //找出递增子序列的长度,其实也就是2进制中1的个数 int cnt = 0; while(x){ if(x&1) cnt++; x >>= 1; } return cnt; } int Replace(int x,int s){ //这里用的不是二分,只是只有1~9这9个数,直接枚举找到存下来即可 for(int i = x; i <= 9; i++) if(s & (1<<i)) return (s^(1<<i))|(1<<x); return s|(1<<x); } LL dfs(int pos, int status, int flag, int limit){ if(pos < 1) return sum(status)==K; if(!limit && dp[pos][status][K] != -1) return dp[pos][status][K]; int len = limit?bit[pos]:9; LL ret = 0; for(int i = 0; i <= len; i++){ ret += dfs(pos-1, (flag&&i==0)?0:Replace(i,status), flag&&(i==0), limit&&i==len); } if(!limit) dp[pos][status][K] = ret; return ret; } LL solve(LL n){ int len = 0; while(n){ bit[++len] = n%10; n /= 10; } return dfs(len, 0, 1, 1); } int main(){ int T; scanf("%d", &T); memset(dp, -1, sizeof(dp)); for(int kase = 1; kase <= T; kase++){ scanf("%I64d %I64d %I64d", &L, &R, &K); printf("Case #%d: %I64d\n", kase, solve(R) - solve(L-1)); } return 0; }