CodeForces 401D Roman and Numbers【数位dp+状态压缩】

来源:互联网 发布:网络咨询工作怎么样 编辑:程序博客网 时间:2024/05/21 23:49

source:

AnnouncementTutorial



题意:给你数n和m,求n的各位重排后有多少个满足膜m==0?

思路:由于n的范围最大可以到18位,故不能暴力枚举(会超时),那么思路就放在了带记忆化的枚举上,数位dp。重点在于如何记忆化,比如现在枚举到了第pos位,然后还需要剩下待定的各位和膜m为mmod,在什么情况下就能直接返回已知结果?当然是当待定的各位还有哪些选择和mmod都确定时结果便确定了:

这便是dp数组:dp[状态(还有那些数待安排)][mmod]

注:此处dp数组并没有pos这一维度,原因在于状态中就已经包含pos此维度了。

而下一个问题是如何表示状态——n的各位中待安排的数,这里就可以用到状态压缩的方式,将状态设成二进制串,每个二进制位对应n的数位上的数,取过就为0,待取就为1。

如此这般,要求的最终结果便是dp[111...1][0]  其中状态是strlen(n)个1


代码如下:

#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;int con[10],a[20];long long dp[(1<<18)+2][105],dec[18];int m,len;long long dfs(int pos,int sta,int mmod,int lead){    int tem[20];    if(pos==-1)    {        if(sta!=0) printf("%d  error!\n",sta);        if(mmod==0) return 1;        else return 0;    }    if(!lead && dp[sta][mmod]!=-1) return dp[sta][mmod];    long long ans=0;    memset(tem,0,sizeof(tem));  //避免重复    for(int i=0;i<=len;i++)    if(sta&(1<<i))    {        if(lead && a[i]==0) continue;        if(tem[a[i]]) continue; //若此位以试探过a[i],则跳过(目的是去重)        tem[a[i]]=1;        ans+=dfs(pos-1,sta-(1<<i),(mmod-dec[pos]*a[i]%m)%m,0);    }    if(!lead) dp[sta][mmod]=ans;    return ans;}long long stat(long long x){    memset(con,0,sizeof(con));    int i=0;    while(x>0)    {        int tem=x%10;        con[tem]++;        a[i++]=tem;        x/=10;    }    len=i-1;    return dfs(len,(1<<i)-1,0,1);}int main(){    long long n;    scanf("%lld%d",&n,&m);    memset(dp,-1,sizeof(dp));    dec[0]=1;    for(int i=1;i<18;i++) dec[i]=dec[i-1]*10;    printf("%lld\n",stat(n));    return 0;}



阅读全文
0 0
原创粉丝点击