题意:给出一个数字num和m,问通过重新排列num中的各位数字中有多少个数(mod m)=0,直接枚举全排列肯定不行,可以用状压dp来搞..
dp[S][k]表示选了num中的S且(mod m)=k的方案种数,初始条件dp[0][0]=1,转移方为dp[i|1<<j[(10*k+num[j])%m]+=dp[i}[k]; ,注意到是多重排列,所以还需要除去重复的。代码如下:
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
ll dp[1<<18][100],c[20];//dp[S][k]表示选了num中的S且(mod m)=k的方案种数
int main(int argc, char const *argv[])
{
char num[20];
int m;
while(cin>>num>>m) {
memset(dp,0,sizeof dp);
memset(c,0,sizeof c);
dp[0][0]=1;
ll div=1,sz=strlen(num),t=1<<sz;
for(int i=0;i<sz;i++) {
div*=(++c[num[i]-='0']);//可重排列最后要除的除数n1!*n2!*...nk!
}
for(int i=0;i<t;i++) {
for(int j=0;j<sz;j++)if(!(i&1<<j)) {//集合S中不含j才转移
if(num[j]||i){//至少一个不为0保证无前导0
for(int k=0;k<m;k++) {
dp[i|1<<j][(10*k+num[j])%m]+=dp[i][k];
}
}
}
}
cout<<dp[t-1][0]/div<<endl;
}
return 0;
}