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

 

posted on 2014-04-22 09:46  BLADEVIL  阅读(429)  评论(0编辑  收藏  举报