lightoj 1021

题目链接:http://lightoj.com/volume_showproblem.php?problem=1021

#include<cstring>
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;

const int maxn = 1<<16;
const int INF = 0x3f3f3f3f;

long long dp[maxn][20];   
//dp[i][j]表示数字集合为i(数位i上为1,代表第i个数取了),对K取余为j的数的各个数位上总的排列数。
int num[20];
int base,K;
char s[20];

int main()
{
  //  freopen("E:\acm\input.txt","r",stdin);
    int T;
    cin>>T;
    for(int cas=1;cas<=T;cas++){
        scanf("%d %d",&base,&K);
        scanf("%s",s);
        int len = strlen(s);
        for(int i=0;i<len;i++){
            if(s[i]>='0' && s[i] <= '9')
                num[i] = s[i] - '0';
            else    num[i] = 10 + s[i] - 'A';
        }
        memset(dp,0,sizeof(dp));
        dp[0][0] = 1;                            //表示各位都没有数字,余数为0的个数有一个,即0.
        for(int i=0;i<(1<<len);i++)              //枚举不同数字集合
            for(int j=0;j<K;j++){                //枚举不同数字集合下的余数情况。
                if(!dp[i][j]) continue;
                for(int k=0;k<len;k++){          //i在第k个数位已经有1,直接跳过。
                    if(i & (1<<k))  continue;
                    dp[i|(1<<k)][(j*base+num[k])%K] += dp[i][j];     
                    //j*base+num[k])%K 表示把第k个数加到dp[i][j]中排列数的末尾,新组成的数对K取余后得到余数。
                    //假设dp[i][j]中的任一个数为num,  则 余数就为(num*base + num[k])% K; 而 num%K == j。 所以利用取模运算就可以化简得到(j*base+num[k])%K   
                }
        }
        printf("Case %d: %lld
",cas,dp[(1<<len)-1][0]);
    }
}
//参考别人的代码写的,非原著。
View Code
原文地址:https://www.cnblogs.com/acmdeweilai/p/3284500.html