题目链接:https://vjudge.net/problem/HDU-4352
题意:题目意思就是给你L到R区间,和一个数字K,然后让你求L到R区间之内满足最长上升子序列长度为K的数字有多少个;
思路:dp[i][j][k]表示前i位,用二进制表示最长上升子序列都出现了那些数,最长上升子序列的长度为K。每次对新加的数字进行判断,看如果他前面一个数字是否比他小,那就替换,没有就将其加入。
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll dp[25][1<<10][25]; int a[20]; ll k; int fun(int s) { int sum=0; while(s) { if(s%2==1) sum++; s/=2; } return sum; } int getnews(int x,int s) { for(int i=x;i<10;i++) { if(s&(1<<i)) return s^(1<<i)|(1<<x); } return s|(1<<x); } //len代表当前枚举的位,s表示哪些数已经选过了的状态,flag代表是否还是处于上边界,qd表示是否为前导0 ll dfs(int len,int s,bool flag,bool qd) { if(len==0) return fun(s)==k; int up=flag?a[len]:9;//决定你枚举的上界是多少 if(!flag&&dp[len][s][k]!=-1) return dp[len][s][k];//如果不是处于上边界并且之前求过了值的话可以直接返回 ll tmp=0; for(int i=0; i<=up; i++) { tmp+=dfs(len-1,(qd&&i==0)?0:getnews(i,s),flag&&i==a[len],qd&&(i==0)); } if(!flag) dp[len][s][k]=tmp;//求了值用dp存下,下次可以使用 return tmp; } ll suan(ll x) { int pos=0; while(x) { a[++pos]=x%10; x/=10; } return dfs(pos,0,true,true); } int main() { ll l,r; memset(dp,-1,sizeof(dp)); int t,u=0; cin>>t; while(t--) { cin>>l>>r>>k; printf("Case #%d: ",++u); cout<<suan(r)-suan(l-1)<<endl; } }