[ SCOI 2007 ] Perm

状压DP解决数字排列问题
本文介绍了一种使用状态压缩动态规划(状压DP)的方法来解决特定数字排列问题,即从给定的数字集中找出能被指定数M整除的不同全排列的数量。文章详细解释了算法思路,并给出了具体的实现代码。

\(\\\)

\(Description\)


给出只包括多个\(0\text~ 9\)的数字集,求有多少个本质不同的全排列,使得组成的数字能够整除\(M\)

  • \(|S|\in [1,10]\)\(M\in [1,10^3]\)

\(\\\)

\(Solution\)


  • 一眼状压,先将所有数字看作互不相同,\(f[S][k]\)表示集合内数字选取情况为\(S\),当前组成的数对\(M\)取模的结果为\(k\)的方案数,显然边界\(f[0][0]=1\)
  • 枚举补集里的元素扩展,每次注意余数改为\((k*10+a[j])\%M\)
  • 注意到每一个答案最后都用到了所有的数,所以所有相同的数最后在每一个答案里都会出现全排列,也就是说答案为\(f[S_{max}][0]\)除掉每一个数的全排列后的答案。

\(\\\)

\(Code\)


#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 1050
#define R register
#define gc getchar
using namespace std;
 
int n,m,t,tot,a[10],p[10],num[10],f[N][N];
 
int main(){
  scanf("%d",&t);
  while(t--){
    char c=gc(); m=0;
    for(R int i=0;i<=9;++i) p[i]=1,num[i]=0;
    while(!isdigit(c)) c=gc();
    a[tot=1]=c^48; ++num[a[1]];
    while(isdigit(c=gc())) ++num[a[++tot]=c^48];
    for(R int i=0;i<=9;++i)
      for(R int j=1;j<=num[i];++j) p[i]*=j;
    while(!isdigit(c)) c=gc();
    while(isdigit(c)){
      m=(m<<1)+(m<<3)+(c^48); c=gc();
    }
    for(R int i=0;i<(1<<tot);++i)
      for(R int j=0;j<m;++j) f[i][j]=0;
    f[0][0]=1;
    for(R int i=0;i<(1<<tot);++i)
      for(R int j=0;j<m;++j) if(f[i][j]){
        for(R int k=0;k<tot;++k)
          if(!(i&(1<<k))) f[i|(1<<k)][(j*10+a[k+1])%m]+=f[i][j];
      }
    for(R int i=0;i<=9;++i) f[(1<<tot)-1][0]/=p[i];
    printf("%d\n",f[(1<<tot)-1][0]);
  }
  return 0;
}

转载于:https://www.cnblogs.com/SGCollin/p/9590136.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值