CodeForces 401D Roman and Numbers【數位dp+狀態壓縮】

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;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章