zoukankan      html  css  js  c++  java
  • 排列

    题目描述

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

    输入格式

    输入第一行是一个整数,表示测试数据的个数,以下每行一组 和 ,中间用空格隔开。保证只包含数字 .

    输出格式

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

    样例

    样例输入

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

    样例输出

    1
    3
    3628800
    90
    3
    6
    1398
    

    数据范围与提示

    样例说明:

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

    数据范围:

    20%的数据满足:(s)的长度不超过5,1<=T<=5
    50%的数据满足:(s)的长度不超过8
    100%的数据满足:(s)的长度不超过10,1<=d<=1000,1<=T<=15

    题解

    • 状压dp,关于状压dp,老师给出了一个很好的判断方法,看数据范围,一般状压dp的数据范围都很小,都在20以内。这是由于状压要开的数组很大。
      关于本题,不好想,但是很好理解。
    • dp数组维二维数组,第一维是状态,这里是拿二进制来存储的,表示每一位表示对应该位置的数是否已经添加。如果是1,就是已经添加,如果为0,就是还未添加。由于10位数,所以数组要开到(1<<10)。第二维是余数。
    • 第一维循环为状态,第二维循环为余数,第三维为要添加的数。
    • 状态转移方程(dp[i|1<<k][(j*10+k)%d] += dp[i][j]),这里面第一维([1|1<<k])为加上k的位置,第二维显然是余数。状压dp的根本为递推,这个式子不难理解。(因为这个递推式中,下标有集合而不是整数,因此需要处理,把这个状态压缩成一个整数,这就是状压dp的名字由来)
    • 转移条件:判断此为是否已经转移过了,如果转移过了,就不需要进一步进行转移。所以式子为if((i&(1<<k))==0),如果判断结果为1,即已经转移过,为0,则执行语句,进行状态转移。
    • 此题注意可以重复选择数字,根据数学知识,如果有数字重复,则除以该数字的阶乘。代码如下:
    • 不回位运算者:请移步 传送门~~~

    code

    #include<bits/stdc++.h>
    using namespace std;
    #define cle(a) memset(a,0,sizeof(a));
    const int maxn=1e5;
    int a[maxn],dp[1<<10][1001],cnt[15];
    int jc(int x){
    	int ans=1;
    	for(int i=1;i<=x;i++) ans*=i;
    	return ans;
    }
    int main(){
    	int T;cin>>T;
    	string s;int d;
    	while(T--){
    		cin>>s>>d;
    		int len=s.length();
    		cle(cnt) cle(dp)
    		for(int i=0;i<len;i++){
    			a[i]=s[i]-'0';
    			cnt[a[i]]++;
    		}
    		int maxs=(1<<len)-1;
    		dp[0][0] = 1;
    		for(int i=0;i<=maxs;i++){
    			for(int j=0;j<d;j++){
    				if(dp[i][j])
    					for(int k=0;k<len;k++)
    						if((i & (1<<k))==0)
    							dp[i|(1<<k)][(j*10+a[k])%d]+=dp[i][j];
    		
    			}
    		}
    		int ans=dp[maxs][0];
    		for(int i=0;i<=9;i++)
    			if(cnt[i]) 
    				ans/=jc(cnt[i]);
    		cout<<ans<<endl;
    	}
    	return 0;
    }
    
  • 相关阅读:
    Linux下MySql多实例免安装部署
    linux apf防火墙安装配置
    Fedora Yum命令查询软件包及清除缓存
    什么时候使用e.target.content和e.target as MovieClip
    帧频计数器
    设置播放器的品质
    访问文件名连续的mc、txt、btn
    Sound 音乐工具类【转载】
    确定字符串中、出现特定字符串的次数
    数组var _list:Array = new Array({logType:"1"});
  • 原文地址:https://www.cnblogs.com/hellohhy/p/13198363.html
Copyright © 2011-2022 走看看