HDU 6148 数位DP

16 篇文章 0 订阅

Valley Numer

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 492    Accepted Submission(s): 259


Problem Description
众所周知,度度熊非常喜欢数字。

它最近发明了一种新的数字:Valley Number,像山谷一样的数字。




当一个数字,从左到右依次看过去数字没有出现先递增接着递减的“山峰”现象,就被称作 Valley Number。它可以递增,也可以递减,还可以先递减再递增。在递增或递减的过程中可以出现相等的情况。

比如,1,10,12,212,32122都是 Valley Number。

121,12331,21212则不是。

度度熊想知道不大于N的Valley Number数有多少。

注意,前导0是不合法的。
 

Input
第一行为T,表示输入数据组数。

每组数据包含一个数N。

● 1≤T≤200

● 1≤length(N)≤100
 

Output
对每组数据输出不大于N的Valley Number个数,结果对 1 000 000 007 取模。
 

Sample Input
  
  
3 3 14 120
 

Sample Output
  
  
3 14 119
 

Source
 

题意 : 定义一种数叫做山峰数,如题所示。很明显的是一道数位DP题

它不可以先增后减,所以我们需要有三种状态,一种是增状态,一种是减状态,一种是初始状态(或不增不减)

这三种状态分别用数字 3 2 4 表示,所以要不出现先减后增的话,就是不记录先3后2这种情况,其他的都记录下来。

DP[当前位][前一位数的值][当前状态]  

一开始状态默认为 4 ,然后出现了非0数之后令它为 2 状态,因为可以先2后3,而不能先3后二,如果一开始就赋值3的话,单调递减的情况会被认为是不符合的,所以要设为2,然后接下来的每次都拿当前数字去和前一位数比较,判断它是增还是减,出了先增后减的情况以外,其他都计数就行了。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
#define ll long long
#define mod 1000000007
char num[200];
ll dp[150][15][5];
ll wei[150];
int len;

ll dfs(ll pos,ll pre,ll now,ll flag){
	if(pos < 0){
		if(now == 4)
			return 0;
		return 1; 
	}
	if(!flag && dp[pos][pre][now] != -1)
		return dp[pos][pre][now];
	ll up = flag ? wei[pos] : 9;
	ll sum = 0;
	for(int i = 0;i <= up;i++){
		if(now == 3 && pre <= i){
			sum += dfs(pos - 1,i,now,flag && i == up) % mod;
			sum %= mod;
		}
		if(now == 2){
			if(pre >= i){
				sum += dfs(pos - 1,i,now,flag && i == up) % mod;
				sum %= mod; 
			}else{
				sum += dfs(pos - 1,i,3,flag && i == up) % mod;
			}
		}
		if(now == 4){
			if(i == 0){
				sum += dfs(pos - 1,i,now,flag && i == up) % mod;
				sum %= mod;
			}else{
				sum += dfs(pos - 1,i,2,flag && i == up) % mod;
				sum %= mod;
			}
		} 
	} 
	if(!flag)
		dp[pos][pre][now] = sum;
	return sum;
}
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		memset(dp,-1,sizeof(dp));
		scanf("%s",num);
		len = strlen(num);
		reverse(num,num + len);
		for(int i = 0;i < len;i++){
			wei[i] = num[i] - '0';
		}
		ll ans = dfs(len - 1,0,4,1) % mod;
		printf("%lld\n",ans);
	}
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值