[Bzoj1072][SCOI2007]排列perm(状压dp)

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1072

状压dp,dp[i][j]表示状态为i,对d取余为j的方案数,则有dp[i|(1<<k)][(j*10+a[k])%d]+=dp[i][j],其中k为不在状态i中的数。

同时,因为有重复的数字,所以最后还要除掉重复数字。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int maxn = 3e5 + 10;
 5 const ll mod = 1e9 + 7;
 6 const ll INF = 1e18;
 7 int dp[1050][1010];
 8 int main() {
 9     int t;
10     scanf("%d", &t);
11     while (t--) {
12         int d;
13         char s[15];
14         int vis[15];
15         int a[15];
16         scanf("%s%d", s, &d);
17         int len = strlen(s);
18         memset(dp, 0, sizeof(dp));
19         memset(vis, 0, sizeof(vis));
20         for (int i = 0; i < len; i++)
21             a[i] = s[i] - '0', vis[a[i]]++;
22         dp[0][0] = 1;
23         for (int i = 0; i < (1 << len); i++) {
24             for (int j = 0; j < d; j++) {
25                 for (int k = 0; k < len; k++) {
26                     if ((i&(1 << k)) == 0)
27                         dp[i | (1 << k)][(j * 10 + a[k]) % d] += dp[i][j];
28                 }
29             }
30         }
31         int ans = dp[(1 << len) - 1][0];
32         for (int i = 0; i <= 9; i++)
33             for (int j = 1; j <= vis[i]; j++)
34                 ans /= j;
35         printf("%d\n", ans);
36     }
37     return 0;
38 }

 

posted @ 2019-07-04 09:08  祈梦生  阅读(144)  评论(0编辑  收藏  举报