题意:求出给定区间[L,R] (0<L<=R<263-1 and 1<=K<=10) 中数满足LIS(非连续严格递增子序列)为K的个数?
如区间[123,321]中的124的LIS为3;
<1> 总结数位DP的优化方式:
例: 1.在codeforces 的beautiful number 中,要求满足能被自己的每一个非零数字整除的数的个数。这时构造出lcm,当高位mod(所有元素的lcm直接优化到2520)后将会出现很多重叠的子问题,这就是状态的转移;当然里面各种的lcm只需存储即可;
2.本题:首先要知道给定一个序列,知道怎么求解LIS,如 1 2 4 6 ,当加入3时,这时长度为3的LIS的最小值为3,而不是4.(使用数组存储的是[长度] = minval),但是本题并不是这样的LIS,这需要模糊看待才能将不一样的高位看成是相同的,才能有重叠的子结构.
在dfs开始时,state为0,这时如果前面加入的是98之后加入1,那么按照原始的LIS长度是为1的,但是这里长度看成3,为什么?因为这里state存储的只是状态,就是说dfs到pos-1位时,只知道前面有哪些值,并不需要知道加入的前后顺序~~那这就将排序问题简化为了组合问题,这才是优化的本质。这也是为什么说state中的1个数就是LIS的值;(故意按照递增排列的)
但是还有问题:
到此,就可以确定dfs的参数为4个pos,state,edge(原来就有),还要考虑一个是否有前置0的zero参数。
(使用的是内置函数__builtin_popcount()来计算数位1的个数)
#include<bits/stdc++.h> using namespace std; #define rep(i,n) for(int (i)= 0;i < (n);i++) #define MS1(a) memset(a,-1,sizeof(a)) typedef long long ll; ll dp[20][1<<10][11]; int bit[20],K; int newstate(int state,int v) { for(int i = v;i < 10;i++) if(state &(1 << i)) return (state ^ (1 << i))|(1<<v); return state|(1<<v); } ll dfs(int pos,int state,bool edge,bool zero) { if(pos == -1) return __builtin_popcount(state) == K; if(!edge && dp[pos][state][K] != -1) return dp[pos][state][K]; int end = edge ? bit[pos]:9; ll ans = 0; for(int i = 0;i <= end;i++){ ans += dfs(pos - 1,(zero && i == 0)?0:newstate(state,i),edge && i == end,zero && i == 0); } if(!edge) dp[pos][state][K] = ans; return ans; } ll calc(ll x) { int tot = 0; while(x){ bit[tot++] = x%10; x /= 10; } return dfs(tot - 1,0,1,1); } int main() { int T,kase = 1; MS1(dp); cin>>T; while(T--){ ll L,R; scanf("%I64d%I64d%d",&L,&R,&K); printf("Case #%d: %I64d\n",kase++,calc(R) - calc(L-1)); } }
原文:http://www.cnblogs.com/hxer/p/5174003.html