BZOJ1072: [SCOI2007]排列perm(状压DP)

题意
对于一个给定的字符串s,问有多少种排列所构成的数字可以被d整除.
解法

我们可以直接爆搜,但是复杂度极高,因为爆搜过程中我们有大量重复过程,因此可以定义一个数组保存状态,dp[s][i] 代表将s的对应的二进制为1对应的数选中后余数为i的状态,例如100101代表已经选中了第1,3,6这三个数;

code
#include <bits/stdc++.h>
using namespace std;
 
char s[21];
int d;
bool vis[21];
int dp[1<<12][1000];
int l;
 
int dfs(int j, int k, int ss) {
    if(dp[ss][k] != -1) {
        return dp[ss][k];
    }
    if(j == l) {
        if(k==0) {
            return 1;
        }
        return 0;
    }
    dp[ss][k] = 0;
    for(int i = 0; i < l; ++ i) {
        if(i>0 && s[i-1]==s[i] && !vis[i-1]) continue;      //因为s种可能出现相同的数,因此为了防止重复我们可以在新的序列中不改变重复的数在原序列中的相对位置
        if(!vis[i]) {
            vis[i] = true;
            dp[ss][k] += dfs(j+1, (k*10+s[i]-'0')%d, ss|(1<<i));
            vis[i] = false;
        }
    }
    return dp[ss][k];
}
 
int main() {
    int t;
    scanf("%d", &t);
    while(t --) {
        scanf("%s%d", s, &d);
        l = strlen(s);
        sort(s, s+l);
        memset(vis, 0, sizeof(vis));
        memset(dp, -1, sizeof(dp));
        printf("%d\n", dfs(0, 0, 0));
    }
    return 0;
}
posted @ 2017-08-25 10:12  zq216991  阅读(110)  评论(0编辑  收藏  举报