「状压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<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<stack>
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<<n)-1;
f[0][0]=1;//0序列为000....的方案肯定只有一种
for(int s=0;s<=maxs;s++){//s代表所选的数的状态,00101代表选了第一个数和第三个数的状态
memset(vis,0,sizeof(vis));//vis记录这个数是否被访问过,因为相同的数会被重复计入方案,
//如,对11230排列,11230和11230会被重复计算,用vis就可以去重,当然也可以不用vis用数学方法去重,即记录所有数出现的个数,最后ans/=cnt[i]!(i=0~9)
for(int i=1;i<=n;i++){ //i表示下一个要选的数
if(s&(1<<(i-1))||vis[a[i]])continue;//s&(i<<(i-1))表示当前枚举的数被包含在了当前状态s里面,不用再次计算
vis[a[i]]=1;
for(int k=0;k<mol;k++){//枚举所有余数k,f[s][k]代表所选的数的状态为s,且余数为k时的总排列数
f[s|(1<<(i-1))][(k*10+a[i])%mol]+=f[s][k];//解释:因为我们的状态是从小到大枚举的,所以f[s][k]会对
//它的所有下一个状态产生影响,而下一个状态的余数恰恰是(k*10+a[i])%mol
//举个例子,序列1234,模数是4,当前状态0011代表12的排列,12会转移到124和123即 原数*10+a[i],相应的余数就会变成 (余数*10+a[i])%mol,即0和3
// cout<<f[s|(1<<(i-1))][(k*10+a[i])%mol]<<endl;
}
}
}
cout<<f[maxs][0]<<endl;
}
}

解法2:暴搜

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

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<stack>
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<str.size();i++){
a[i]=str[i]-'0';
sum[a[i]]++;
}
DFS(1,0);
cout<<ans<<endl;
}
}

解法3:next_permutation生成全排列

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

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

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

atol:将字符串转换为数列

详见代码

代码1:不用atol正常写

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<stack>
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<n;i++){
a[i]=str[i]-'0';
}
sort(a,a+n);//严格要求必须递增,否则全排列不对
do{
long long temp=0;
for(int i=0;i<n;i++)temp=temp*10+a[i];//全排列是保存在数组a里的,通过这种方法取出来
if(temp%mol==0)ans++;
}while(next_permutation(a,a+n));
cout<<ans<<endl;
}
}

代码2:用atol

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<stack>
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<<ans<<endl;
}
}

「状压DP」「暴力搜索」排列perm的更多相关文章

  1. 「BZOJ 5010」「FJOI 2017」矩阵填数「状压DP」

    题意 你有一个\(h\times w\)的棋盘,你需要在每个格子里填\([1, m]\)中的某个整数,且满足\(n\)个矩形限制:矩形的最大值为某定值.求方案数\(\bmod 10^9+7\) \(h ...

  2. UVaLive 6625 Diagrams & Tableaux (状压DP 或者 DFS暴力)

    题意:给一个的格子图,有 n 行单元格,每行有a[i]个格子,要求往格子中填1~m的数字,要求每个数字大于等于左边的数字,大于上边的数字,问有多少种填充方法. 析:感觉像个DP,但是不会啊...就想暴 ...

  3. 「BZOJ 5161」最长上升子序列「状压DP」

    题意 求一个\(1\sim n\)的排列LIS的期望长度,\(n\leq 28\) 题解 考虑朴素的LIS:\(f[i] = min(f[j]) + 1\) 记\(mx[i]\)为\(f\)的前缀最大 ...

  4. ☆ [POJ2411] Mondriaan's Dream 「状压DP」

    传送门 >Here< 题意:用1*2的砖块铺满n*m的地板有几种方案 思路分析 状压经典题! 我们以$f[i][j]$作为状态,表示第i行之前全部填完并且第i行状态为j(状压)时的方案数. ...

  5. 「CF744C」Hongcow Buys a Deck of Cards「状压 DP」

    题意 你有\(n\)个物品,物品和硬币有\(A\),\(B\)两种类型,假设你有\(M\)个\(A\)物品和\(N\)个\(B\)物品 每一轮你可以选择获得\(A, B\)硬币各\(1\)个,或者(硬 ...

  6. 【bzoj5161】最长上升子序列 状压dp+打表

    题目描述 现在有一个长度为n的随机排列,求它的最长上升子序列长度的期望. 为了避免精度误差,你只需要输出答案模998244353的余数. 输入 输入只包含一个正整数n.N<=28 输出 输出只包 ...

  7. 「算法笔记」状压 DP

    一.关于状压 dp 为了规避不确定性,我们将需要枚举的东西放入状态.当不确定性太多的时候,我们就需要将它们压进较少的维数内. 常见的状态: 天生二进制(开关.选与不选.是否出现--) 爆搜出状态,给它 ...

  8. 「PKUSC2018」最大前缀和(状压dp)

    前言 考试被\(hyj\)吊着打... Solution 考虑一下如果前缀和如果在某一个位置的后面的任意一个前缀和都<=0,肯定这就是最大的. 然后这样子就考虑左右两边的状压dp,然后就好了. ...

  9. loj2540 「PKUWC2018」随机算法 【状压dp】

    题目链接 loj2540 题解 有一个朴素三进制状压\(dp\),考虑当前点三种状态:没考虑过,被选入集合,被排除 就有了\(O(n3^{n})\)的转移 但这样不优,我们考虑优化状态 设\(f[i] ...

随机推荐

  1. js-ajax方法详解以及封装

    本文主要从使用ajax请求的步骤.ajax状态码和http响应状态码以及ajax封装三个方面阐述 一.使用ajax请求的步骤 // 一.创建 XMLHttpRequest 对象 var xhr = n ...

  2. (七)DVWA之SQL Injection--SQLMap测试(Low)

    目录结构 一.测试需求分析 二.SQLMap利用SQL注入漏洞,获取数据库信息 1.判断是否存在注入点 2.获取DBMS中所有的数据库名称 3.获取Web应用当前连接的数据库 4.列出数据库中的所有用 ...

  3. .Net Core实战之基于角色的访问控制的设计

    前言 上个月,我写了两篇微服务的文章:<.Net微服务实战之技术架构分层篇>与<.Net微服务实战之技术选型篇>,微服务系列原有三篇,当我憋第三篇的内容时候一直没有灵感,因此先 ...

  4. Censoring【KMP算法+堆栈模拟】

    Censoring 传送门:链接   来源:UPC8203 题目描述 Farmer John has purchased a subscription to Good Hooveskeeping ma ...

  5. 链式前向星存树图和遍历它的两种方法【dfs、bfs】

    目录 一.链式前向星存图 二.两种遍历方法 一.链式前向星存图:(n个点,n-1条边) 链式前向星把上面的树图存下来,输入: 9 ///代表要存进去n个点 1 2 ///下面是n-1条边,每条边连接两 ...

  6. 2019-02-04 Linux的一些常用命令学习

    今天在电脑里用装了个ubuntu的虚拟机,学习了一下基本操作 ls ls用于查看文件夹的文件 mkdir mkdir可以创建一个文件夹(directory) cd 目录切换 pwd print wor ...

  7. fiddler修改请求参数

    1.打开fiddler ,点击界面左侧左侧底部 2.此图标为before request请求(修改请求参数时,设置这个,可以修改请求参数) 3..再次点击该按钮,将图标切换到下图after respo ...

  8. CAS(乐观锁)与ABA问题

    cas是什么 CAS 全称 compare and swap 或者compare and exchange  比较并且交换.用于在没有锁的情况下,多个线程对同一个值的更新. cas原理 例如,我们对一 ...

  9. .Net Core服务监控报警指标上报Prometheus+Grafana

    前言 简单集成Prometheus+Grafana,指标的上报收集可视化. Prometheus Prometheus是一个监控平台,监控从HTTP端口收集受监控目标的指标.在微服务的架构里Prome ...

  10. 四分位数与pandas中的quantile函数

    四分位数与pandas中的quantile函数 1.分位数概念 统计学上的有分位数这个概念,一般用p来表示.原则上p是可以取0到1之间的任意值的.但是有一个四分位数是p分位数中较为有名的. 所谓四分位 ...