您的位置:首页 > 其它

HDU 4352 数位DP + LIS

2017-08-14 22:22 330 查看

数位DP + LIS

题意:

​ 每一个数字都有最长上升子序列,给出一个范围,求出里面的最长上升子序列为k 的数字个数。

思路:

​ 数位DP枚举的是每一个数位上的所有情况,当枚举到一个数字的时候其最长上升子序列可以压缩在一个二进制中,而二进制1出现的次数就是最长上升子序列的长度,其思想和LIS 的思想抑制。

定义:dp[i][s][k] 表示从最高位到第i位数字组成的数字最长上升子序列的长度为k的个数。

而在dfs(pos,s,z,e) 的时候pos表示数字的第几位,s表示组成的序列状态(不断更新),z表示上一位是否为0(关乎到s的更新,如果之前都是0的话s = 0),e表示是否为边界(判断是否可以记忆化搜索)。

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

typedef long long LL;

const int maxn = 20;

LL n,m,k;
int bit[maxn];
LL dp[maxn][1<<10][maxn];

int Getnum(int x)
{
int ans = 0;
while(x) {
if(x&1) ans++;
x >>= 1;
}
return ans;
}

int GetNew(int s,int x)
{
for(int i = x;i < 10; i++)
if(s&(1<<i)) return (s^(1<<i) | (1<<x));
return s|(1<<x);
}

LL dfs(int pos,int s,int z,int e)
{
if(pos < 0) return Getnum(s) == k;
if(!e && dp[pos][s][k] != -1) return dp[pos][s][k];
int End = e ? bit[pos] : 9;
LL ans = 0;
for(int i = 0;i <= End; i++) {
ans += dfs(pos-1,(z&&i==0)?0:GetNew(s,i),z&&i==0,e&&i==End);
}
if(!e) dp[pos][s
4000
][k] = ans;
return ans;
}

LL solve(LL x)
{
int pos = 0;
while(x) {
bit[pos++] = x%10;
x /= 10;
}
return dfs(pos-1,0,1,1);
}

int main(int argc, char const *argv[])
{
// freopen("in.txt","r",stdin);

memset(dp,-1,sizeof(dp));
int ncase = 1,tt;
scanf("%d",&tt);
while(tt--) {
scanf("%I64d%I64d%I64d",&n,&m,&k);
printf("Case #%d: %I64d\n",ncase++,solve(m) - solve(n-1));
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dp