数谜

数谜

题目描述

数谜

小x认为自己是数学爱好者,但大Y并不这样认为。于是大Y让小x找出有多少个自然数接近n。我们认为一个数p在m下接近n,当且仅当:

  • 在十进制下,重新组织p的各位数字,可以使p变为n(举个例子:重新组织数字120,可且仅可得到120,102,210,201);
  • p没有前导0;
  • p是m的倍数。

小x当然会算,但结果太大了,于是来找你帮忙。

输入格式

一行,两个正整数n,m。m ≤ 100。

输出格式

一行,一个数表示你给小x的答案。

输入样例1

104 2
输出样例1
3
样例解释1

这三个数满足条件:104, 140, 410。

输入样例2
223 4
输出样例2
1
样例解释2

只有一个数232在4下接近223。

测试点

n

1~2

<105

3~4

<1010

5~10

<1018

数据范围





    本题求解方案数,而且每次在数的末尾加上一个数,具有明显阶段性,因而用动态规划来解决。

    可以发现,n<1018, 因此可以使用状压DP。用set表示当前状态:其中的1表示这位数已经被用过,0则反之。

    但是,题目中要求p要是m的倍数,似乎不太好判断是否合法。其实稍微转化一下,就是%m等于0的方案数。因而在DP的时候记录上这一余数,就能打破限制

    此外,即使set相同,但由于不同的排列方式,模m的余数也很有可能不相同,是不同的状态。所以余数应该记为一维。

    于是,定义f[set][mod]表示 当前状态为set,并且当前组成的数模m等于mod的方案数。


    状态转移:

    外重循环枚举i ,当前是第几位数

        嵌套枚举前一次的状态lset,其中lset中1必须为i-1个

         嵌套枚举上次的余数lmod

                嵌套枚举这一位选的数now

[当i==1时,不能选0。而且,由于每种数可能有多个(比如有3个1, 不管选1的顺序如何都是同一种方案),会造成重复。因而要规定顺序:每种数必须从第一个开始选起]

    f[newset][newmod]+=f[lset][mod];

    最后的答案即为f[(1<<位数)-1][0];

    代码如下:

#include
   
   
    
    
#include
    
    
     
     
#include
     
     
      
      
using namespace std;
const long long MAXN=20,MAXS=(1<<18),MAXM=105;
int m,num;
long long f[MAXS][MAXM];//状态为set,%m的余数。 
long long a[MAXN],nums[MAXS],n,t;
void init()
{
	t=n,num=0;
	while(t)
	{
		a[++num]=t%10;
		t/=10;
	}
	sort(a+1,a+1+num);
}
void prepare()
{
	for(int i=0;i<(1<
      
      
       
       0)	nums[i]++;
		}
	}
}
void dp()
{
	f[0][0]=1;
	for(int i=1;i<=num;i++)//枚举到了第i位 
	{
		for(int set=0;set<(1<
       
       
         0) continue; if(i==1&&a[now+1]==0) continue; if(now!=0&&a[now]==a[now+1]&&(set&(1<<(now-1)))==0) continue; f[set+(1< 
        
          >n>>m; init(); prepare(); dp(); cout< 
         
           < 
          
            < 
            
           
          
         
       
      
      
     
     
    
    
   
   
    

         看到题目,一定要注意题目是否具有阶段性,考虑DP求解。在数据量较小,不超过20时,想想状压DP。

   在题目限制棘手的时候,要懂得转换。如果某个限制似乎要通过枚举才能解决,那就运用种种定理,想想办法让它在递推时能够转移。

   其实,这题写到这并没有想象中的那么难,状压DP也是之前学过的,考试时为什么怎么都想不到呢?一个是看到位数为18,却没有往状压DP上想过。另外,也没有想过用在DP时记录余数的办法去除倍数这一限制。

   总而言之,还是对DP掌握不够好,同时处理问题不够灵活啊。。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值