「状压DP」「暴力搜索」排列perm

「状压DP」「暴力搜索」排列

题目描述:

题目描述

给一个数字串 s 和正整数 d, 统计 sss 有多少种不同的排列能被 d 整除(可以有前导 0)。例如 123434 有 90 种排列能被 2 整除,其中末位为 2 的有 30 种,末位为 4 的有 60 种。

输入格式

输入第一行是一个整数 TTT,表示测试数据的个数,以下每行一组 s 和 d,中间用空格隔开。s 保证只包含数字 0,1,2,3,4,5,6,7,8,9

输出格式

每个数据仅一行,表示能被 d 整除的排列的个数。

输入输出样例

输入 #1

7
000 1
001 1
1234567890 1
123434 2
1234 7
12345 17
12345678 29

输出 #1

1
3
3628800
90
3
6
1398

说明/提示

100% 的数据满足:s 的长度不超过 10,1≤d≤1000,1≤T≤15。

在前三个例子中,排列分别有 1,3,36288001 种,它们都是 1 的倍数。

解法1:状压DP

思路:

s的长度很短,不是暴搜就是状压,然鹅这道题都可以用

大多数状压中都是当前某个状态对之后于此相关状态产生影响

因此可以对整个序列的长度进行状压,详见代码

代码:

#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=(1<<10)+5,INF=0x3f3f3f3f;
int n,m,f[maxn][1000],sum[11],mol,a[maxn],vis[maxn];
char str[1000];
inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
int main(){
	freopen("a.in","r",stdin);
	int t=read();
	while(t--){
		memset(f,0,sizeof(f));
		memset(a,0,sizeof(a));
		memset(sum,0,sizeof(sum));
		scanf("%s",str);
		int n=strlen(str);
		mol=read();
		for(int i=1;i<=n;i++){
			a[i]=str[i-1]-'0';
		}
		int maxs=(1<

解法2:暴搜

相对于状压而言,暴搜更好想一些,就是从低位依次枚举至高位,但是时间消耗更大,洛谷上会T两个点,可能剪剪枝会过

#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=(1<<10)+5,INF=0x3f3f3f3f;
int n,m,f[maxn][1000],sum[11],mol,a[maxn],vis[maxn],ans;
char str[1000];
inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
void DFS(int now,long long x){//看数据范围,不开long long会炸,x代表当前的数,now代表当前位数
	if(now>n){//now>n说明x是末状态
		if(x%mol==0)ans++;
		return;
	}
	for(int i=0;i<=9;i++){
		if(sum[i]){
			sum[i]--;//为保证当前这一位选取这个元素对这一位选取其他元素没影响,所以自减后要自加回来
			DFS(now+1,x*10+i);
			sum[i]++;
		}
	}
}
int main(){
	freopen("a.in","r",stdin);
	int t=read();
	while(t--){
		memset(a,0,sizeof(a));
		memset(sum,0,sizeof(sum));
		string str;
		cin>>str;
		mol=read();
		n=str.size();
		ans=0;
		for(int i=0;i

解法3:next_permutation生成全排列

应该所有人都想过用全排列来写,但是时间开销很大,吾日观洛谷,发现STL中的几个比较有趣的函数:

next_permutation:从原递增序列中求出所有全排列

prev_permutation:从原递减序列中求出所有全排列

atol:将字符串转换为数列

详见代码

代码1:不用atol正常写

#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=(1<<10)+5,INF=0x3f3f3f3f;
int n,m,f[maxn][1000],sum[11],mol,a[maxn],vis[maxn],ans;
char str[1000];
inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
int main(){
	freopen("a.in","r",stdin);
	int t=read();
	while(t--){
		memset(a,0,sizeof(a));
		memset(sum,0,sizeof(sum));
		string str;
		cin>>str;
		mol=read();
		n=str.size();
		ans=0;
		for(int i=0;i

代码2:用atol

#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=(1<<10)+5,INF=0x3f3f3f3f;
int n,m,f[maxn][1000],sum[11],mol,a[maxn],vis[maxn],ans;
char str[1000];
inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
int main(){
	freopen("a.in","r",stdin);
	int t=read();
	while(t--){
		memset(a,0,sizeof(a));
		memset(sum,0,sizeof(sum));
		char str[maxn];//string排序排的是字符串,char排的是字符
		cin>>str;
		mol=read();
		n=strlen(str);
		ans=0;
		sort(str,str+n);
		do{
			long long temp=atoll(str);//直接转
			
			if(temp%mol==0)ans++;
		}while(next_permutation(str,str+n));
		cout<

你可能感兴趣的:(「状压DP」「暴力搜索」排列perm)