「状压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<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的更多相关文章
- 「BZOJ 5010」「FJOI 2017」矩阵填数「状压DP」
题意 你有一个\(h\times w\)的棋盘,你需要在每个格子里填\([1, m]\)中的某个整数,且满足\(n\)个矩形限制:矩形的最大值为某定值.求方案数\(\bmod 10^9+7\) \(h ...
- UVaLive 6625 Diagrams & Tableaux (状压DP 或者 DFS暴力)
题意:给一个的格子图,有 n 行单元格,每行有a[i]个格子,要求往格子中填1~m的数字,要求每个数字大于等于左边的数字,大于上边的数字,问有多少种填充方法. 析:感觉像个DP,但是不会啊...就想暴 ...
- 「BZOJ 5161」最长上升子序列「状压DP」
题意 求一个\(1\sim n\)的排列LIS的期望长度,\(n\leq 28\) 题解 考虑朴素的LIS:\(f[i] = min(f[j]) + 1\) 记\(mx[i]\)为\(f\)的前缀最大 ...
- ☆ [POJ2411] Mondriaan's Dream 「状压DP」
传送门 >Here< 题意:用1*2的砖块铺满n*m的地板有几种方案 思路分析 状压经典题! 我们以$f[i][j]$作为状态,表示第i行之前全部填完并且第i行状态为j(状压)时的方案数. ...
- 「CF744C」Hongcow Buys a Deck of Cards「状压 DP」
题意 你有\(n\)个物品,物品和硬币有\(A\),\(B\)两种类型,假设你有\(M\)个\(A\)物品和\(N\)个\(B\)物品 每一轮你可以选择获得\(A, B\)硬币各\(1\)个,或者(硬 ...
- 【bzoj5161】最长上升子序列 状压dp+打表
题目描述 现在有一个长度为n的随机排列,求它的最长上升子序列长度的期望. 为了避免精度误差,你只需要输出答案模998244353的余数. 输入 输入只包含一个正整数n.N<=28 输出 输出只包 ...
- 「算法笔记」状压 DP
一.关于状压 dp 为了规避不确定性,我们将需要枚举的东西放入状态.当不确定性太多的时候,我们就需要将它们压进较少的维数内. 常见的状态: 天生二进制(开关.选与不选.是否出现--) 爆搜出状态,给它 ...
- 「PKUSC2018」最大前缀和(状压dp)
前言 考试被\(hyj\)吊着打... Solution 考虑一下如果前缀和如果在某一个位置的后面的任意一个前缀和都<=0,肯定这就是最大的. 然后这样子就考虑左右两边的状压dp,然后就好了. ...
- loj2540 「PKUWC2018」随机算法 【状压dp】
题目链接 loj2540 题解 有一个朴素三进制状压\(dp\),考虑当前点三种状态:没考虑过,被选入集合,被排除 就有了\(O(n3^{n})\)的转移 但这样不优,我们考虑优化状态 设\(f[i] ...
随机推荐
- 四、归并排序 && 快速排序
一.归并排序 Merge Sort 1.1.实现原理 如果要排序一个数组,我们先把数组从中间分成前后两部分,然后对前后两部分分别排序,再将排好序的两部分合并在一起,这样整个数组就都有序了. 归并排序使 ...
- ESXI多网卡网络配置
1.两台路由器接入不同网络: 2.一台4网口服务器,网口分别为:vmnic0.vmnic1.vmnic2.vmnic3 3.ESXI6.5服务器虚拟机系统 测试环境模拟: 路由1:192.168.0. ...
- Okapi BM25算法
引言 Okapi BM25,一般简称 BM25 算法,在 20 世纪 70 年代到 80 年代,由英国一批信息检索领域的计算机科学家发明.这里的 BM 是"最佳匹配"(Best M ...
- httpd解析php的小贴士
以前使用php的时候, 都是直接用nginx和php对接的, 极少是用apache去对接, 但是最近在用httpd测试WordPress的时候, 发现一个有趣的问题, php不用启动也能直接去解析ph ...
- 重学 Java 设计模式:实战组合模式(营销差异化人群发券,决策树引擎搭建场景)
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 小朋友才做选择题,成年人我都要 头几年只要群里一问我该学哪个开发语言,哪个语言最好. ...
- 扩展.Django-权限系统
目录 Django权限系统auth User 新建用户 认证用户 修改密码 登录 退出登录 只允许登录的用户访问 Group Permission 检查用户权限 管理用户权限 自定义权限 Django ...
- monkey命令的基本使用
看到monkey,你想到了什么?今天给大家分享下monkey命令的基本使用 monkey测试是Android平台自动化测试的一种手段,通过monkey程序模拟用户触摸屏幕,滑动.按键操作等操作对设备上 ...
- Spring系列.依赖注入配置
依赖注入的配置 Spring的依赖注入分为基于构造函数的依赖注入和基于setter方法的依赖注入. 基于构造函数的依赖注入 <!-- 通过构造器参数索引方式依赖注入 --> <bea ...
- Vue —— VueX精讲(1)
大纲 这一讲我们最主要的就是学习vue中的数据管理VueX,这个是一个大杀器 一.回顾一些Promise相关的东西 Promise 有几个比较重要的方法,最重要的还是有一个叫做all的方法,这个也是非 ...
- 2019-02-08 Python学习之Scrapy的简单了解
今天遇到的问题和昨天差不多,一个Scrapy装了好久,anaconda卸了又装,pycharm卸了又装,环境变量配置一堆,依赖包下载一堆.查了一堆资料总算是搞好了. Scripy: 先放个框架结构图( ...