HDU - 4352(数位DP+状态压缩+LIS)

题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=4352

题意:在给定区间L-R内有多少数满足长度为K的最长上升子序列。

LIS算法可以事先去了解一下,eg: 3,5,8,9的序列,现在碰到了6,则对序列更新为3,5,6,9,

(LIS基于DP的思想,优化后是如上所示的贪心策略加二分,时间复杂度有n*n到n*logn);

又因为是由0-9的数字,转为二进制不超过2的10次幂(1024),对所有的进行二进制的状态压缩,更新思想也是基于LIS的贪心思想,

dp[i][j][k]表示第i位数,状态为j时的LIS为k的数量,以此去记录状态,每换新长度时都必须初始化状态因为是一个新的开始。

可以看代码具体实现。

first作用为判断是都前缀0,即可以理解为数字都没有被放入,limit即是限制数字大小。

update中 s(当前状态)&1<<i 即是比较有没有(2的i次幂)这一大小的的值,即有没有数字i,没有的话i++,继续向前找,如果有的话,s^1<<i 即找到大于等于x的(^即是异或运算),s^1<<i后(即消除它)或运算1<<x(当前的数放入),要是没有的话,直接s|1<<x(即x为最大的放入数字更新)。要是这个过程还不懂的话,自己模拟一下LIS算法的如上面的eg所示的例子,其实也就是思想。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<vector>
#include<algorithm>
#include<queue>
#define MAX_len 50100*4
using namespace std;
typedef long long ll;
const ll mod=1000000007;
ll k;
int a[70];
ll dp[30][1<<10][15];
int bit(int state)
{
    int cnt=0;
    while(state>0)
    {
        if(state&1==1)
            cnt++;
        state>>=1;
    }
    return cnt;
}
int update(int x,int s)
{
    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 sta,bool first,bool limit)
{
	if(pos<1)
    return  bit(sta)==k;
    if(!limit&&dp[pos][sta][k]!=-1)
         return dp[pos][sta][k];
    ll temp=0;
    int  up=limit?a[pos]:9;
    for(int i=0;i<=up;i++)
    {
        temp+=dfs(pos-1,(first&&(i==0))?0:update(i,sta),first&&(i==0),limit&&i==up);
    }
    if(!limit)
        dp[pos][sta][k]=temp;
    return temp;
}
ll solve(ll n)
{ 
	int len=1;
	while(n)
	{
		a[len++]=n%10;
		n/=10;
	}
	return dfs(len-1,0,1,true);
}
int main()
{
	memset(dp,-1,sizeof(dp));
	int T;
	scanf("%d",&T);
	int yy=1;
	while(T--)
	{
		ll l,r;
		scanf("%I64d %I64d %I64d",&l,&r,&k);
		printf("Case #%d: ",yy++);
		printf("%I64d\n",solve(r)-solve(l-1));
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值