您的位置:首页 > 其它

HDU 4352 XHXJ's LIS (数位DP,状压)

2015-10-05 15:11 253 查看
题意:

  前面3/4的英文都是废话。将一个正整数看成字符串,给定一个k,问区间[L,R]中严格的LIS=k的数有多少个?

思路:

  实在没有想到字符0~9最多才10种,况且也符合O(nlogn)求LIS的特点,所以用状态压缩可以解决。

  看到状态压缩的字眼基本就会做了,增加一维来保存当前LIS的状态。由于求LIS时的辅助数组d[i]表示长度为i的LIS最后一个元素,d数组是严格递增的,所以好好利用d数组的特性来设计状态压缩才是关键。压缩的状态0101可以表示:仅有0和2在数组d中,即d[1]=0,d[2]=2的意思。状态的设计方法有多种。

  此题在考虑前导零问题时,逐个枚举位数,可以这样做是因为如果位数超过了1,则最后一个数位若为0是不会对结果构成影响的,因为最后的0都不会被考虑在LIS中。而对于那些个位数为0(或者说后缀0)会对结果产生影响的,最好是不要这样用了(例如spoj Balanced Numbers就不可以)。

#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <map>
#include <algorithm>
#include <vector>
#include <iostream>
#define pii pair<int,int>
#define INF 0x7f3f3f3f
#define LL long long
#define ULL unsigned long long
using namespace std;
const double PI  = acos(-1.0);
const int N=20;

LL f
[1<<11][11], bit
;
//[位数][状态][k]

int insert(int s,bool flag,int pos) //修改状态
{
for(int i=pos; i<=9&&flag; i++)   //找到第一位>=pos的,抹去
if(s&(1<<i))
{
s^=(1<<i);
break;
}
return s|(1<<pos);
}

int gethigh(int s)  //获取LIS最大元素,即d[len]。
{
for(int i=9; i>=0; i--)    if(s&(1<<i))    return i;
return -1;
}

LL dfs(int i,int up,int s,int k,bool e)
{
//up为总位数,s为状态,k为仍需一段len=k的串来组成LIS=K的
if(i==0)               return k==0;
if(i<k)                return 0;    //剩下的位数已不够k个,不能组成LIS=k
if(!e && ~f[i][s][k] ) return f[i][s][k];

LL ans=0;
int d= i==up? 1: 0;     //为了解决前缀0的情况,起始不为0
int u= e? bit[i]: 9;

int h=gethigh(s);       //LIS的最大元素
for( ; d<=u; d++)
{
if(d>h) ans+=dfs(i-1,up,insert(s,0,d),k-1,e&&d==u);
else    ans+=dfs(i-1,up,insert(s,1,d),k,e&&d==u);   //LIS长度不变
}
return e? ans: f[i][s][k]=ans;
}

LL cal(LL n,int k)
{
if(n==0)    return 0;
int len=0;
while(n)    //拆数
{
bit[++len]=n%10;
n/=10;
}
LL ans=0;
for(int i=k; i<len; i++)    //为了解决前导0问题,逐个枚举
ans+=dfs(i,i,0,k,false);
if(len>=k)
ans+=dfs(len,len,0,k,true);
return ans;
}

int main()
{
//freopen("input.txt","r",stdin);
memset(f, -1, sizeof(f));
LL L, R;int t, K, Case=0;
cin>>t;
while( t-- )
{
scanf("%lld%lld%d",&L,&R,&K);
printf("Case #%d: %lld\n", ++Case, cal(R,K)-cal(L-1,K));
}
return 0;
}


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