Codeforces 259 D 数位DP + 搜索

本文详细介绍了数位DP算法的递归和非递归两种实现方式,通过具体代码实例深入探讨了如何解决特定类型的问题。

http://www.codeforces.com/contest/259/problem/D

数位DP可以写成递归和非递归两种:

递归:

#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL __int64
const LL mod = 1000000007;
LL dp[11], ans;
int len;
char a[11];

void add(int dep, int dig, int num)
{
    if(!dep)
    {
        dp[dig] += num;
        return;
    }
    add(dep-1, dig+1, num*2);
    add(dep-1, dig, num*8);
}
void dfs(int dep, int sum, int tot)
{
    int i;
    if(sum >= len) return;
    if(dep == 6)
    {
        for(i = sum+1; i <= len; i++)
            ans = (ans + tot*dp[i])% mod;
        return;
    }
    for(i = 0; i < len; i++)
    {
        if(dp[i])
        {
            dp[i]--;
            dfs(dep+1, sum+i, tot*(dp[i]+1)%mod);
            dp[i]++;
        }
    }
}
int main()
{
    int i, j, t = 0;
    len = strlen(gets(a));
    for(i = 0; i < len; i++)
    {
        for(j = 0; j < a[i] - '0'; j++)
        {
            if(j == 4 || j == 7)
                add(len-1-i, t+1, 1);
            else 
                add(len-1-i, t, 1);
        }   
        if(a[i] == '4' || a[i] == '7')
                t++;
    }
    dp[t]++, dp[0]--;
//  for(i = 0; i <= 9; i++)
//      printf("%d\n", dp[i]);
    dfs(0, 0, 1);
    printf("%I64d\n", ans);
    return 0;
}

非递归:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define LL __int64
LL dp[11][11], f[11];
int a[11], n;
const LL mod = 1000000007;
void init()
{
	int i, j, k;
	dp[1][1] = 2; dp[1][0] = 8;
	for(i = 2; i <= 9; i++)
	{
		for(j = 1; j <= i; j++)
			dp[i][j] = (dp[i-1][j] * 8 + dp[i-1][j-1] * 2) % mod;
		dp[i][0] = dp[i-1][0] * 8 % mod;
	}
//	for(i = 2; i <= 9; i++)
//		for(j = 0; j <= i; j++)
//			printf("dp[%d][%d] = %I64d\n", i, j, dp[i][j]);
}

LL ans;
int l;
void dfs(int dep, int sum, LL tot) 
{
	int i;
	if(dep == 6)
	{
		for(i = sum+1; i <= l; i++)
			ans = (ans + tot * f[i]) % mod;
		return;
	}
	if(sum >= l) return;
	for(i = 0; i <= l; i++)
		if(f[i])
		{
			f[i]--;
			dfs(dep+1, sum+i, tot*(f[i]+1)%mod);
			f[i]++;
		}
}
int main()
{
	init();
	int i, j, k;
	scanf("%d", &n); 
	while(n) {a[l++] = n % 10; n /= 10;}
	//处理位数< l 的 数
	for(j = 0; j <= l-1; j++)
		f[j] = (f[j] + dp[l-1][j]) % mod;
//	for(i = 0; i <= 9; i++)
//		printf("f[%d] = %I64d\n", i, f[i]);
	LL cnt = 0;
	for(i = l-1; i >= 0; i--) // 每一位降位处理
	{
		for(j = (i == l-1 ? 1 : 0); j < a[i]; j++) // 注意首位不能为0,很容易出错
		{
			if(j==4 || j==7) 
				for(k = cnt+1; k <= cnt+i+1; k++) // 注意lucky number 的 范围
					f[k] = (f[k] + (i ? dp[i][k-1-cnt] : 1)) % mod;
			else 
				for(k = cnt; k <= cnt+i; k++) // 注意lucky number 的 范围
					f[k] = (f[k] + (i ? dp[i][k-cnt] : 1)) % mod;
		}

	//	for(j = 0; j <= 9; j++)
	//	printf("i = %d f[%d] = %I64d\n", i, j, f[j]);
		cnt += (a[i]==4 || a[i]==7); 
	}
	f[cnt]++; 
	if(l > 1)f[0]--; //特别注意 输入的数只有一位的时候, 我们没有 把 0 算进去,所以不用去零。
//	for(i = 0; i <= 9; i++)
//		printf("f[%d] = %I64d\n", i, f[i]);
	dfs(0, 0, 1);
	printf("%I64d\n", ans);
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值