HDU - 4352 XHXJ's LIS

题意:

  给定范围[L, R] 和 k(1~10)求范围内数位的LIS(最长上升子序列)等于k的数的个数。

思路:

  普通的LIS做法是对于当前的数,在模拟数列中找到第一个比他大的并替换掉。

  在数位DP中需要状态压缩,其实对于每个数只有两种状态,即在模拟数列中或者不在。那么就可以用二进制(1<<10)来表示总的状态。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int t, k;
int bits[20];
ll l, r;
ll dp[20][1<<10][11];
bool judge(int state) {
    int cnt = 0;
    for(int i = 0; i < 10; i++)
    if(state & (1<<i)) cnt++;
    if(cnt == k) return 1;
    return 0;
}
int change(int state, int pos) {
    int poi = -1;
    for(int i = pos; i < 10; i++) 
    if(state & (1<<i)) {
        poi = i;
        break;
    }
    if(~poi) state -= (1<<poi);
    state += (1<<pos);
    return state;
}
ll dfs(int pos, int state, bool limit) {
    if(pos < 0) return judge(state);
    if((!limit) && (~dp[pos][state][k])) return dp[pos][state][k];
    int up = limit?bits[pos]:9;
    ll res = 0;
    for(int i = 0; i <= up; i++) 
    res += dfs(pos-1, state==0&&i==0?0:change(state, i), limit && up==i);
    if(!limit) dp[pos][state][k] = res;
    return res;
}
ll solve(ll x) {
    int pos = 0;
    while(x) {
        bits[pos++] = x%10;
        x /= 10;
    }
    return dfs(pos-1, 0, 1);
}
int main() {
    memset(dp, -1, sizeof(dp));
    scanf("%d", &t);
    for(int casee = 1; casee <= t; casee++) {
        scanf("%lld%lld%d", &l, &r, &k);
        printf("Case #%d: %lld\n", casee, solve(r) - solve(l-1));
    }
}
View Code

 

posted @ 2017-12-26 23:27  Pneuis  阅读(201)  评论(0编辑  收藏  举报