zoukankan      html  css  js  c++  java
  • Crime HDU

    题目链接:HDU - 4623

    题意:将1~n,n个数重新排列组合,使得每相邻的两个数互质;问总共有多少中方案;

    思路:n最大是28,首先想到状压DP,2^28=268435456,肯定会爆栈;所以还需要优化一下;通过观察可以发现,质因子相同的数可以看做一类;也就是说,6, 12, 24可以放到一组中,用一位表示,那么,将28个数分组后就构成了每个位不同进制的数字,由计算的共state=1727999种状态;时间复杂度:O(state*28*28), 大约1354751216;

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=2e6;
    //Ꮧ对1~28分组,有相同质因子的为一组,又1, 17, 19, 23与任何数互质,所以将其分为一组,在同一组有相同的对外性;
    int group[]={0, 0, 1, 2, 1, 3, 4, 5, 1, 2, 6, 7, 4, 8, 9, 10, 1, 0, 4, 0, 6, 11, 12, 0, 4, 3, 13, 2, 9};
    //提取每一组的代表数;
    int digit[]={1, 2, 3, 5, 6, 7, 10, 11, 13, 14, 15, 21, 22, 26};
    //prime[i][j]表示i组的数与j组的是否互质;
    bool prime[20][20];
    int gcd(int a, int b){
    	return b==0?a:gcd(b, a%b);
    }
    //初始化prime数组;
    void init(){
    	//这里为什么i,j都是0~13?可以保留疑问,继续向下看;
    	for(int i=0; i<14; i++){
    		for(int j=0; j<14; j++){
    			prime[i][j]=(gcd(digit[i], digit[j])==1?1:0);
    		}
    	}
    }
    //bit[i]表示i位是bit[i]进制, num表示给出的范围总共包括的组数,也就表示一共有多少位;
    int bit[20], dp[maxn][20], num, mod;
    //计算cnt数组的状态下,表示的变进制数;cnt[i]表示第i为是cnt[i];
    int get_state(int *cnt){
    	int state=0;
    	for(int i=0; i<=num; i++){
    		state=state*bit[i]+cnt[i];
    	}
    	return state;
    }
    //计算在state状态下的cnt数组;
    void get_cnt(int state, int *cnt){
    	for(int i=num; i>=0; i--){
    		cnt[i]=state%bit[i];
    		state/=bit[i];
    	}
    }
    //suf[i]表示在第i位加1,十进制数增加suf[i]。例如二进制100=4,1000=8;
    int suf[20];
    void get_suf(){
    	suf[num]=1;
    	for(int i=num-1; i>=0; i--){
    		suf[i]=suf[i+1]*bit[i+1];
    	}
    }
    int solve(int state){
    	memset(dp, 0, sizeof(dp));
    	get_suf();
    	int cnt[20];
    	memset(cnt, 0, sizeof(cnt));
    	for(int i=0; i<=num; i++){
    		cnt[i]=1;
    		dp[get_state(cnt)][i]=bit[i]-1;///dp[state][j]表示在state状态下,排列是以j为最后一个数字
    		cnt[i]=0;
    	}
    	for(int k=1; k<=state; k++){
    		get_cnt(k, cnt);
    		for(int i=0; i<=num; i++){
    			if(cnt[i]==0) continue;//如果i位没有选数就构不成以i为结尾就跳过;这里表示在state状态下最后选的是i组的数,然后接下来枚举j作为新状态结尾,这个j和i互质;
    			for(int j=0; j<=num; j++){
    				if(!prime[i][j]||cnt[j]>=bit[j]-1) continue;//如果i,j组数不互质或者j组无数可选了,就不选j组数;细心的朋友会发现0组咋办?即如果之前选了1,此时还能选19,所以prime[0][0]是等于1的;
    				int s=k+suf[j];
    				dp[s][j]=(dp[s][j]+dp[k][i]*(bit[j]-cnt[j]-1)%mod)%mod;///新状态就是旧状态乘以j的剩余数量,剩余x个那肯定是结尾有x种搭配嘛
    			}
    		}
    	}
    	int ans=0;
    	for(int i=0; i<=num; i++){
    		ans=(ans+dp[state][i])%mod;
    	}
    	return ans%mod;
    }
    int main(){
    	int T;
    	scanf("%d", &T);
    	init();
    	while(T--){
    		int n;
    		scanf("%d%d", &n, &mod);
    		num=0;
    		for(int i=1; i<=n; i++){
    			num=max(num, min(i, group[i]));///取出有多少种不同的集合,例如 0, 11,2,那么num=3;
    		}
    		int cnt[20];
    		memset(cnt, 0, sizeof(cnt));
    		for(int i=1; i<=n; i++){
    			cnt[group[i]]++;
    		}
    		for(int i=0; i<=num; i++){
    			bit[i]=cnt[i]+1;///因为我们有可不选这个状态,所以bits就应该cnt+1,
    		}
    		printf("%d
    ", solve(get_state(cnt)));
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    数据库字段太多,批量快速建立实体类方法(适合大量字段建立实体类)
    SQL service 中的 ”输入SQL命令窗口“ 打开了 “属性界面” 回到 ”输入SQL命令窗口“
    计算机软件编程英语词汇集锦
    编程常用英语词汇
    svn上传和下载项目
    当启动tomcat时出现tomcat setting should be set in tomcat preference page
    Implicit super constructor Object() is undefined for default constructor. Must define an explicit constructor
    eclipse中选中一个单词 其他相同的也被选中 怎么设置
    Spring Boot的@SpringBootApplication无法引入的问题
    最全的SpringCloud视频教程
  • 原文地址:https://www.cnblogs.com/hgangang/p/12400479.html
Copyright © 2011-2022 走看看