转载请注明:http://blog.csdn.net/jiangshibiao/article/details/23100595
【原题】
【大意】给你一个n,让你找有多少x满足:x mod m=0。其中x是n的一个排列。重复的只计算一种。
【分析】这真是一道状态压缩的好题目。什么是状态压缩呢?就是把一个状态串压成一个十进制数。在这题里,对于n中的每一位数,都能表示成0或1(取或是不取)。而且总的状态数只有2^len-1(len是n的位数),因此我们可以用DP推出每一种状态。f[i][j]表示在i这种状态(01串的十进制形式),余数为j时的方案数。显然,如果我想取当前的数第K个数——dight[k],f[i][j]就能推到f[i+2^(k-1)][(j*10+dight[k])%m]。当然,要注意这个位置是否已经用过,同时也要注意数字的判重情况。
【代码】
#include<cstdio>
#include<cstring>
using namespace std;
const int size=19;
bool flag[10];
long long dight[size],f[1<<size][101],i,j,p,m,k,n,t;
int main()
{
scanf("%I64d%I64d",&p,&m);
while (p)
{
dight[++n]=p%10;
p/=10;
}
for (i=1;i<=n/2;i++) t=dight[i],dight[i]=dight[n-i+1],dight[n-i+1]=t;
f[0][0]=1;
for (i=0;i<(1<<n)-1;i++) //枚举每一种状态。而且可以证明,到某一种状态时,它一定在之前被推完了。
for (j=0;j<m;j++)
{
memset(flag,0,sizeof(flag)); //防止出现相同的情况。
if (f[i][j]==0) continue; //这种状态本身已经推不到。
for (k=1;k<=n;k++)
if (!flag[dight[k]]) //某一个数码没有出现过。
{
if (i&(1<<(k-1))) continue; //这一位已经用过了
if (i==0&&dight[k]==0) continue; //前导0的问题
flag[dight[k]]=true;
f[i+(1<<(k-1))][(j*10+dight[k])%m]+=f[i][j]; //递推
}
}
printf("%I64d",f[(1<<n)-1][0]);
return 0;
}