• 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;
    }
    

      

  • 相关阅读:
    Java中关于String类型的10个问题
    关于Linux中后台运行程序(&)退出时收不到SIGHUP信号的说明
    《Javascript DOM编程艺术》学习笔记 第8章 充实文档的内容
    《Javascript DOM编程艺术》学习笔记 第7章 动态创建标记
    《Javascript DOM编程艺术》学习笔记 第1-6章
    golang: 读取已关闭的缓冲型channel的表现
    关于《汇编语言(王爽)》程序6.3使用16个dw 0的问题
    关于寄存器的一些笔记
    img格式镜像转ISO格式
    深入理解计算机操作系统:第1章 计算机系统漫游(学习笔记)
  • 原文地址:https://www.cnblogs.com/hgangang/p/12400479.html
Copyright © 2020-2023  润新知