bzoj1072Perm——状压DP
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1072
数字串最多只有10位,所以考虑用状压;
压缩的状态是哪些数字被用过,这样可以从一种状态加一个数字转移到另一种状态;
求能整除一个数的个数,那么当然记录当前状态下mod该数的余数,则在一种状态末尾加上另一个数时,这一维的转移就可以方便地表示;
然而因为把每个数看做是不同的,所以这样对于重复出现的数则计算了多次:
比如有两个1,则所有排列中这两个1都会交换位置被算两遍,所以最终答案要/2;
也就是这么多个数的全排列,它的阶乘,所以最后答案要除以所有这些阶乘来去重;
注意循环的内外层关系。
这位的博客写得颇为详细:https://blog.csdn.net/wzq_qwq/article/details/46366399
代码如下:
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> using namespace std; int t,d,a[15],f[1500][1005],up,s[15]; int jc[15]={1,1,2,6,24,120,720,5040,40320,362880,3628800}; char dc[15]; int main() { scanf("%d",&t); while(t--) { memset(s,0,sizeof s); memset(f,0,sizeof f); cin>>dc; int ln=strlen(dc); for(int i=1;i<=ln;i++) { a[i]=dc[i-1]-'0'; s[a[i]]++; } scanf("%d",&d); up=(1<<ln)-1; f[0][0]=1; for(int j=0;j<=up;j++)//顺序 for(int l=0;l<d;l++) { if(!f[j][l])continue;//剪枝 for(int i=1;i<=ln;i++) if((j&(1<<(i-1)))==0) { int k=j|(1<<(i-1)); f[k][(l*10+a[i])%d]+=f[j][l]; } } for(int i=0;i<=9;i++) if(s[i])f[up][0]/=jc[s[i]]; printf("%d\n",f[up][0]); } return 0; }