2017广东工业大学程序设计竞赛决赛:G. 等凹数字(数位DP)

本文介绍了一种特殊数字——等凹数字的概念及其计数算法。等凹数字是指从高位到低位,数字先非递增再非递减,并且还是回文数。文章通过数位DP方法解决了一个具体问题:给定区间内有多少个等凹数字。


Problem G: 等凹数字

Description

定义一种数字称为等凹数字,即从高位到地位,每一位的数字先非递增再非递减,不能全部数字一样,且该数是一个回文数,即从左读到右与从右读到左是一样的,仅形成一个等凹峰,如5432123455544334455是合法的等凹数字,543212346123321,111111不是等凹数字。现在问你[L,R]中有多少等凹数字呢?

Input

第一行一个整数T,表示数据的组数。

接下来T行每行俩个数字L和R,(1<=L<=R<=1e18)

Output

 输出一个整数,代表[L,R]中有多少等凹数字

Sample Input

2
1 100
101 200

Sample Output

0
1

HINT

 小于等于2位的数字无凹峰


感觉很多数位dp都可以直接用这道题的程序改

因为这道题又有递增又要递减,还加个回文

复杂但难度也不是特别高

dp[len][pos][pre][up][down][hw]

分别表示:当前有效长度(去掉前导0后);枚举到了第pos位;上一个数字是pre

是否递增过;是否递减过;是否回文

#include<stdio.h>
#include<string.h>
#define LL long long
long long str[20], temp[20], dp[20][20][10][3][3][3];
long long Sech(LL len, LL pos, LL pre, LL up, LL down, LL hw, LL flag, LL k)
{
	LL u, i, ans;
	if(pos<0)
	{
		if(up && down && hw)
			return 1;
		return 0;
	}
	if(flag==0 && k==0 && dp[len][pos][pre][up][down][hw]!=-1)
		return dp[len][pos][pre][up][down][hw];
	if(flag==1)  u = str[pos];
	else  u = 9;
	ans = 0;
	for(i=0;i<=u;i++)
	{
		temp[pos] = i;
		if(k==1)
		{
			ans += Sech(len-(k && i==0), pos-1, i, 0, 0, hw, flag && i==u, k && (i==0));
			continue;
		}
		if(i==pre)
		{
			if(hw && pos<(len+1)/2)
				ans += Sech(len, pos-1, i, up, down, i==temp[len-pos], flag && i==u, k && (i==0)); 
			else
				ans += Sech(len, pos-1, i, up, down, hw, flag && i==u, k && (i==0));
		}
		else if(i>pre)
		{
			if(down==0)
				continue;
			if(hw && pos<(len+1)/2)
				ans += Sech(len, pos-1, i, 1, down, i==temp[len-pos], flag && i==u, k && (i==0));
			else
				ans += Sech(len, pos-1, i, 1, down, hw, flag && i==u, k && (i==0));
		}
		else if(i<pre)
		{
			if(up)
				continue;
			if(hw && pos<(len+1)/2)
				ans += Sech(len, pos-1, i, up, 1, i==temp[len-pos], flag && i==u, k && (i==0));
			else
				ans += Sech(len, pos-1, i, up, 1, hw, flag && i==u, k && (i==0));
		} 
	}
	if(flag==0 && k==0)
		dp[len][pos][pre][up][down][hw] = ans;
	return ans;
}
long long Jud(long long x)
{
	LL len;
	len = 0;
	while(x)
	{
		str[len++] = x%10;
		x /= 10;
	}
	return Sech(len-1, len-1, 0, 0, 0, 1, 1, 1);
}
int main(void)
{
	LL T, l, r;
	scanf("%d", &T);
	while(T--)
	{
		memset(dp, -1, sizeof(dp));
		scanf("%lld%lld", &l, &r);
		printf("%lld\n", Jud(r)-Jud(l-1));
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值