[SCOI2007]排列
[SCOI2007]排列
题意:
给一个数字串 \(s\) 和正整数 \(d\), 统计 \(s\) 有多少种不同的排列能被 \(d\) 整除,可以有前导 \(0\) 。
分析:
看最多只有 \(10\) 位,我们考虑状压:
设 \(dp[S][i]\) 表示枚举到集合为 \(S\) 时形成的数,模 \(d\) 为 \(k\) 的情况,则有
首先,我们枚举所有的集合 \(S\) ,然后再枚举所有没有被选的数 \(j\) ,再枚举余数 \(k\) 即可转移。
转移方程为:
dp[S|1<<(j-1)][(k*10+a[j])%d]+=dp[S][k];//添加进去这个数
考虑重复排列,因为当前要填的数字,很有可能要填好几遍,所以用一个 \(vis\) 数组判断当前数有没有选择。
然后直接顺着枚举就行了
#include<bits/stdc++.h>
using namespace std;
const int N=12,M=1500;
int T,d,a[N],cnt,dp[M][M];
bool vis[N];
char s[N];
void init(){
memset(dp,0,sizeof(dp)); dp[0][0]=1;
cnt=0;
}
int main(){
cin>>T;
while(T--){
init();
scanf("%s%d",s+1,&d);
int len=strlen(s+1);
for(int i=1;i<=len;i++) a[i]=s[i]-'0';
for(int S=0;S<(1<<len)-1;S++){//考虑每一种情况
memset(vis,0,sizeof(vis));
for(int j=1;j<=len;j++)
if(!(S&(1<<(j-1)))&&!vis[a[j]]){//没有转移过,而且没有选择
vis[a[j]]=1;
for(int k=0;k<d;k++)//k表示对d取余的数
dp[S|1<<(j-1)][(k*10+a[j])%d]+=dp[S][k];//添加进去这个数
}
}
printf("%d\n",dp[(1<<len)-1][0]);
}
system("pause");
return 0;
}
不关注的有难了😠😠😠https://b23.tv/hoXKV9

浙公网安备 33010602011771号