HDU - 4352 - XHXJ's LI(数位dp)

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;
}

猜你喜欢

转载自blog.csdn.net/henu_jizhideqingwa/article/details/82889585