HDU 5898 数位DP

题意: 求满足数位上连续的奇数为偶数个,连续的偶数为奇数个的数的个数。
思路: 典型的数位DP,dp[i][p][l]一维表示数位第i位,二维表示上一位的奇偶性,三维表示在第i位之前保持上一位的奇偶性的长度的奇偶性。

我的第一层传入的p和l为1,0。因为0个奇数是满足偶数个奇数的性质的,即数位上只有奇数个偶数的的数是满足条件的。


#include <iostream>
#include <string.h>
#include <cstdio>
#define LL long long
using namespace std;
int wei[30];
LL dp[30][2][2];	//后两维仅用来判断奇偶足够 
LL dfs(int pos, int limit, int lead, int p, int l)
{
	if(pos == -1)
	{
		if(lead) return 1;			
		//都是前导零即0满足,没这句也是可以的。如果前面所有的位都是前导零的话, 
		//传到pos=-1位p=1,l=0正好是满足偶数个奇数的性质的,其实0本身就是这性质 
		if(p && !(l&1)) return 1;	
		if(!p && (l&1)) return 1;
		return 0;
	}
	if(!lead && !limit && dp[pos][p][l&1] != -1) return dp[pos][p][l&1];
	int up = limit? wei[pos]: 9; LL res = 0;
	for(int i = 0; i <= up; ++i)
	{
		//pos位之前都是前导零,所以不会致使l增长,也会保持pos位前是偶数个奇数的特质
		if(lead && i==0)
		{ 
			res += dfs(pos-1, limit&&i==wei[pos], 1, 1, 0);
			continue;
		}
		//前面是奇数且i是奇数,所以直接可往后走 
		if(p && (i&1)) 
		res += dfs(pos-1, limit&&i==wei[pos], lead&&i==0, 1, l+1);
		
		//前面是奇数而i变为偶数,只有满足前面为偶数个奇数才能往后走 
		if(p && !(l&1) && !(i&1)) 
		res += dfs(pos-1, limit&&i==wei[pos], lead&&i==0, 0, 1);
		
		//前面是偶数且i是偶数,所以直接可往后走 
		if(!p && !(i&1)) 
		res += dfs(pos-1, limit&&i==wei[pos], lead&&i==0, 0, l+1);
		
		//前面是偶数而i变为奇数,只有满足前面为奇数个偶数才能往后走 
		if(!p && (l&1) && (i&1)) 
		res += dfs(pos-1, limit&&i==wei[pos], lead&&i==0, 1, 1);
		
	}
	if(!lead && !limit && dp[pos][p][l&1] == -1) dp[pos][p][l&1] = res;
	return res;
}
LL solve(LL k)
{
	if(k == 0) return 1;
	int cnt = 0;
	while(k)
	{
		wei[cnt++] = k%10;
		k /= 10;
	}
	return dfs(cnt-1, 1, 1, 1, 0);
}
int main()
{
	int t;
	LL l, r;
	memset(dp, -1, sizeof dp);
	scanf("%d", &t);
	for(int i = 1; i <= t; ++i)
	{
		scanf("%lld %lld", &l, &r);
		printf("Case #%d: %lld\n", i, solve(r)-solve(l-1));
	}
	return 0;
}

继续加油~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值