LA 7344 Numbered Cards(数位DP)

题意:给定n,选择一些小于等于n的数构成一个集合,使得集合内各个元素的十进制表示形式没有重叠的数字,问这样的集合有多少个。


分析:一共十个数字,我们可以枚举单个数字的所有组合情况(一千种),然后用数位DP求出符合每种组合的所有单个数字的个数,设f[x]表示数字集合为x的个数,我们可以利用用状压DP的方式求出f[x]的值。


#include<iostream>
#include<string>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<set>
#include<map>
#include<vector>
#include<cstring>
#include<stack>
#include<cmath>
#include<queue>
#include <unordered_map>
#define INF 0x3f3f3f3f
#define eps 1e-9  
#define MOD 1000000007 
using namespace std;
typedef long long ll;
int T,n,a[20],top[1050];  
ll dp[20][1050],f[1050],sum[1050];
ll dfs(int pos,int sta,int limit,int lead,int num)
{
	if(pos == -1)
	{
		if(sta == num) return 1ll;
		return 0ll;
	}
	if(!limit && !lead && dp[pos][sta] >= 0) return dp[pos][sta];
	int up = limit ? a[pos] : 9;
	ll ans = 0;
	for(int i = 0;i <= up;i++)
	 if((1<<i) & num || (!i && lead))
	 {
	 	ans = (ans + dfs(pos-1,sta | ((lead && !i) ? 0 : (1<<i)),limit && i == up,lead && !i,num)) % MOD;	
	 }	
	if(!limit && !lead) dp[pos][sta] = ans; 
	return ans;
}
ll solve(int x,int num)
{
	int pos = 0;
    while(x)
    {  
        a[pos++] = x % 10;
        x /= 10;  
    }  
    memset(dp,-1,sizeof(dp));
    return dfs(pos-1,0,1,1,num);
}
int main()
{
	int tot = (1<<(10))-1;
	for(int i = 1;i <= tot;i++) top[i] = (1<<(int)log2(i));
	scanf("%d",&T);
	for(int t = 1;t <= T;t++)
	{
		scanf("%d",&n);
		ll ans = 0ll;
		memset(f,0,sizeof(f));
		for(int i = 1;i <= tot;i++) sum[i] = solve(n,i);
		for(int i = 1;i <= tot;i++)
		{
			int res = i - top[i];
			for(int j = res;j;j = (j-1)&res)
			 f[i] = (f[i] + f[j]*sum[i-j]) % MOD; 
			f[i] = (f[i] + sum[i]) % MOD;
			ans = (ans + f[i]) % MOD;
		}
		printf("Case %d: %lld\n",t,ans);
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值