hdu4352(数位dp)
题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=4352
题意:求区间L到R之间的数A满足A的的数位的最长递增序列的长度为K的数的个数。
分析:数位dp,dp[i][j][k]表示后面还有i位,此时状态为k,最长上升子序列为j时的总数(在非限制即0~9任意填的情况下)。
要真正理解LIS的本质才能解这题,state状态维护的是前面上升子序列中出现的数字(二进制状态压缩),前面设状态为167(state为001100001),假设此时i=2,维护上升序列长度为3,应该把6变为2(此时state为001000011)127,最长上升子序列长度不变,但能让后面更多的数加进来。
这题还得注意前导0的影响。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <cstdio> #include <cstring> #include <string> #include <cmath> #include <iostream> #include <algorithm> #include <queue> #include <cstdlib> #include <stack> #include <vector> #include <set> #include <map> #define LL long long #define mod 100000000 #define inf 0x3f3f3f3f #define eps 1e-9 #define N 100010 #define FILL(a,b) (memset(a,b,sizeof(a))) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 using namespace std; int dig[30]; LL dp[25][11][1100]; int k;//当前位,上升子序列出现的数字的状态,长度,是否上限,是否前导0 LL dfs(int pos,int state,int num,int limit,int fzore) { if(!pos) { return k==num; } if(!limit&&~dp[pos][k][state])return dp[pos][k][state]; int len=limit?dig[pos]:9; LL ans=0; for(int i=0;i<=len;i++) { if((1<<i)>state) ans+=dfs(pos-1,(fzore&&!i)?0:state|(1<<i),(fzore&&!i)?0:num+1,limit&&i==len,fzore&&!i); else if(state&(1<<i)) ans+=dfs(pos-1,state,num,limit&&i==len,fzore&&!i); else { for(int j=i+1;j<=9;j++) if(state&(1<<j)) { ans+=dfs(pos-1,state-(1<<j)|(1<<i),num,i==len&&limit,fzore&&!i); break; } } } if(!limit)dp[pos][k][state]=ans; return ans; } LL solve(LL x) { int len=0; while(x) { dig[++len]=x%10; x/=10; } return dfs(len,0,0,1,1); } int main() { int T,cas=1; FILL(dp,-1); scanf("%d",&T); while(T--) { LL a,b; scanf("%I64d%I64d%d",&a,&b,&k); printf("Case #%d: ",cas++); printf("%I64d\n",solve(b)-solve(a-1)); } }