bzoj 1072 状压DP

  我们用w[i][j]来表示,i是一个二进制表示我们选取了s中的某些位,j表示这些位%d为j,w[i][j]则表示这样情况下的方案数,那么我们可以得到转移.w[i|(1<<k)][(j*10+s[k]-'0')%d]+=w[i][j]。

  假设s中有x个3,那么我们算出的状态中同样的数我们算了x!次,最后除掉就好了。

/**************************************************************

    Problem: 1072

    User: BLADEVIL

    Language: C++

    Result: Accepted

    Time:476 ms

    Memory:12680 kb

****************************************************************/

 

//By BLADEVIL

#include <cstdio>

#include <cstring>

 

using namespace std;

 

int d,cnt[11],w[3010][1010];

char s[11];

 

int main() {

    int task; scanf("%d",&task);

    while (task--) {

        scanf("%s%d",s,&d); int len=strlen(s);

        memset(cnt,0,sizeof cnt);

        for (int i=0;i<len;i++) cnt[s[i]-'0']++;

        for (int i=0;i<(1<<len);i++) 

            for (int j=0;j<d;j++) w[i][j]=0;

        w[0][0]=1;

        for (int i=0;i<(1<<len);i++)

            for (int j=0;j<d;j++) if (w[i][j])

                for (int k=0;k<len;k++) if (!(i&(1<<k)))

                    w[i|(1<<k)][(j*10+s[k]-'0')%d]+=w[i][j];

        int ans=w[(1<<len)-1][0];

        //printf("%d\n",ans);

        for (int i=0;i<10;i++)

            for (int j=1;j<=cnt[i];j++) {

                ans/=j ;

            }

        printf("%d\n",ans);

    }

    return 0;

}

 

你可能感兴趣的:(ZOJ)