http://acm.hdu.edu.cn/showproblem.php?pid=4352
题意:
问一个区间内数字的每个数,看成一个数列,求最长上升子序列为k的数的个数
思路:
一开始不会写,后来看了别人的博客感觉二进制真的吊,
奥秘全在这里面
int getStatus(int status, int x) {
for (int i = x; i <= 9; i ++) {
if(status & (1 << i))
return (status ^ (1 << i)) | (1 << x);
}
return status | (1 << x);
}
拿一个数列a[]={1,2,3,3,6,5,4}举例,求最长上升子序列
因为数字都是0-9之内的数,那么最多才有2^10中状态,初始的状态Status=0
status:0000000000
第一个数,x为1,status为0,i从0->9没有出现status&(1 << i) 为1的情况,那么输出status|(1 << x) ,
status:0000000010
第二个数,x为2,status为2,i从0->9没有出现status&(1 << i) 为1的情况,那么输出status|(1 << x) ,
status:0000000110
第三个数,x为3,status为6,i从0->9没有出现status&(1 << i) 为1的情况,那么输出status|(1 << x) ,
status:0000001110
第四个数,x为3,status为14,i在3是status&(1<<i)为1,那么(status^(1<<i))|(1<<x)
status:0000001110
第五个数,x为6,status为14,i从0->9没有出现status&(1 << i) 为1的情况,那么输出status|(1 << x) ,
status:0001001110
第6个数,x为5,status为78,i在6时就返回了(status^(1<<i))|(1<<x)
status:0000101110
第7个数时,x为4,status为46,i在5时就返回了(status^(1<<i))|(1<<x)
status:0000011110
。。。
从上诉例子可以看出,求最长上升子序列时,status的位数就是序列的长度,无论发生什么情况,位数都代表的数长度
那么既然知道了这个东西,我们就可以用数位dp来模拟,此过程
AC代码
#include <iostream>
#include <string.h>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <stdio.h>
#include <deque>
using namespace std;
#define LL long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
#define inf 1000000000000000000
#define maxn 1005
#define eps 0.00000001
#define PI acos(-1.0)
#define M 1000000007
int num[25];
LL dp[25][1<<12][15];
int k;
int getNum(int status) {
int cnt = 0;
for (int i = 0; (1 << i) <= status; i ++)
if((1 << i) & status) cnt ++;
return cnt;
}
int getStatus(int status, int x) {
for (int i = x; i <= 9; i ++) {
if(status & (1 << i))
return (status ^ (1 << i)) | (1 << x);
}
return status | (1 << x);
}
LL dfs(int pos, int m, int status, bool limit) {
if(!pos) return getNum(status) == k;
if(!limit && dp[pos][status][k] != -1) return dp[pos][status][k];
int endi = limit ? num[pos] : 9;
LL ans = 0;
int status1;
for (int i = 0; i <= endi; i ++) {
if(m && !i)
status1 = 0;
else
status1 = getStatus(status, i);
ans += dfs(pos-1, m && !i, status1, limit && i == endi);
}
return limit ? ans : dp[pos][status][k] = ans;
}
LL Solve(LL n) {
num[0] = 0;
while(n) {
num[++ num[0]] = n % 10;
n /= 10;
}
return dfs(num[0], 1, 0, 1);
}
int main(int argc, const char * argv[]) {
int T, Case = 0;
scanf("%d", &T);
memset(dp, -1, sizeof(dp));
while(T --) {
LL l, r;
scanf("%lld %lld %d", &l, &r, &k);
printf("Case #%d: %lld\n", ++Case, Solve(r) - Solve(l - 1));
}
return 0;
}