题意:
给定范围[L, R] 和 k(1~10)求范围内数位的LIS(最长上升子序列)等于k的数的个数。
思路:
普通的LIS做法是对于当前的数,在模拟数列中找到第一个比他大的并替换掉。
在数位DP中需要状态压缩,其实对于每个数只有两种状态,即在模拟数列中或者不在。那么就可以用二进制(1<<10)来表示总的状态。

#include <bits/stdc++.h> using namespace std; typedef long long ll; int t, k; int bits[20]; ll l, r; ll dp[20][1<<10][11]; bool judge(int state) { int cnt = 0; for(int i = 0; i < 10; i++) if(state & (1<<i)) cnt++; if(cnt == k) return 1; return 0; } int change(int state, int pos) { int poi = -1; for(int i = pos; i < 10; i++) if(state & (1<<i)) { poi = i; break; } if(~poi) state -= (1<<poi); state += (1<<pos); return state; } ll dfs(int pos, int state, bool limit) { if(pos < 0) return judge(state); if((!limit) && (~dp[pos][state][k])) return dp[pos][state][k]; int up = limit?bits[pos]:9; ll res = 0; for(int i = 0; i <= up; i++) res += dfs(pos-1, state==0&&i==0?0:change(state, i), limit && up==i); if(!limit) dp[pos][state][k] = res; return res; } ll solve(ll x) { int pos = 0; while(x) { bits[pos++] = x%10; x /= 10; } return dfs(pos-1, 0, 1); } int main() { memset(dp, -1, sizeof(dp)); scanf("%d", &t); for(int casee = 1; casee <= t; casee++) { scanf("%lld%lld%d", &l, &r, &k); printf("Case #%d: %lld ", casee, solve(r) - solve(l-1)); } }