HDOJ-ACM1016(JAVA) 字典序全排列,并剪枝

转载声明:原文转自http://www.cnblogs.com/xiezie/p/5576273.html

题意:

一个环是用图中所示的n个圆组成的。把自然数1、2、……、n分别放入每个圆中,并在相邻的圆中的数值总和为一个质数。
注:第一圈数应该是1。

输出:

输出格式显示为下面的示例。每一行代表在环里圆中的数从1开始顺时针和逆时针。数字的数量必须满足上述要求。按字典顺序打印解决方案。
你是写一个程序,完成上述过程。
每一种情况下打印一条空白线。

题目分析:

首先,因为需要遍历多次,质数不可能每次判断的时候都重新计算,应该生产质数表(0<n<20,因此质数表中的质数小于40)。

java算法如下:

    /*
     * 输入数字,作为序号传入
     * 判断是否质数
     * */
    static boolean isAPrimeInForty(int num){
        if(isPrimes[num]==1){
            return true;
        }
        return false;
    }

    /*
     * 生产序号为0-39的0,1序列
     * 1代表为质数
     * 0代表为非质数
     * */
    static int[] isPrimes = createPrimes();

    static int[] createPrimes(){
        int[] primes = new int[40];
        for(int i = 2 ; i != 40 ; i ++ ){
            if(isAPrime(i)){
                primes[i] = 1;
            }
        }
        return primes;
    }

    //判断是否质数
    static boolean isAPrime(int num){
        int len = (int) (Math.sqrt(num)+1);
        for(int i = 2 ; i != len ; i ++){
            if(num%i==0){
                return false;
            }
        }
        return true;
    }

解题:

第一次,由于最近搞了全排列,我的解题思路是:将所有数进行字典序全排列(由于数字是顺序输入数组,我在字典序排列中省去了排序的过程),并判断是否符合题中条件。如果是则存储(因此,顺时针逆时针都包括)或者直接输出。

结果:Time Limit Exceeded----------不过我相信这样做答案一定是正确的

代码如下:

import java.util.*;

import java.io.*;

public class Main{

    public static void main(String[] arg){
        Scanner scan = new Scanner(new BufferedInputStream(System.in));
        int n;
        int count = 1;
        while(scan.hasNextInt()){
            n = scan.nextInt();
            System.out.println("Case "+ count+++ ":" );
            int[] nums = new int[n];
            for(int i = 0 ; i != n; i ++){
                nums[i] = i+1;
            }
            getAllPermutationsAndCheck(nums,1,n-1);
            System.out.println();
        }
        scan.close();
    }

    static boolean isAnswer(int[] nums){//判断数组是否符合条件
        int len = nums.length;
        for(int i = 0 ; i != len ; i ++ ){
            int sum  = nums[i%len] + nums[(i+1)%len];
            if(!isAPrimeInForty(sum)){
                return false;
            }
        }
        return true;
    }

    static boolean isAPrime(int num){//判断是否质数
        int len = (int) (Math.sqrt(num)+1);
        for(int i = 2 ; i != len ; i ++){
            if(num%i==0){
                return false;
            }
        }
        return true;
    }

    static void getAllPermutationsAndCheck(int[] cs,int start,int len){//cs为字典序数组
        if(cs==null)
            return;
        while(true)
        {
            if(isAnswer(cs)){
                for(int j = 0 ; j != cs.length; j ++){
                    if( j == cs.length -1){
                        System.out.print(cs[j]);
                        break;
                    }
                    System.out.print(cs[j] + " ");
                }
                System.out.println();
            }
            int j=start+len-2,k=start+len-1;
            while(j>=start && cs[j]>cs[j+1])
                --j;
            if(j<start)
                break;

            while(cs[k]<cs[j])
                --k;

            swap(cs,k,j);

            int a,b;
            for(a=j+1,b=start+len-1;a<b;++a,--b)
            {
                swap(cs,a,b);
            }
        }
    }

    static boolean isAPrimeInForty(int num){//判断是否质数
        for(int i = 0 ; i != 12 ; i ++ ){
            if(num>primes[i]){
                continue;
            }
            if(num==primes[i]){
                return true;
            }
            if(num<primes[i]){
                return false;
            }
        }
        return false;
    }

    static int[] primes = createPrimes();

    static int[] createPrimes(){
        int[] primes = new int[12];
        int count = 0;
        for(int i = 2 ; i != 40 ; i ++ ){
            if(isAPrime(i)){
                primes[count] = i;
                count++;
            }
        }
        return primes;
    }

    static void swap(int[] nums , int i , int j){
        int t;
        t = nums[i];
        nums[i] = nums[j];
        nums[j] = t;
    }
}

当然,当我全排列10个数的时候,秒输出,当我全排列12个数的时候,半天都输出不了~

只能说明:全排列是最差的暴力破解,没有进行剪枝

下面一个思路是:深度搜索

  同样是字典序全排列(不过是从1-n的全排列,有了具体场景)

  但是在字典序全排列过程中,进行剪枝,遇到一个前后相加不为质数的就跳过(省去了后面的全排列)

结果:Accepted    执行占用时间:2698MS   执行占用内存:13940K

具体java算法如下:

import java.util.*;

import java.io.*;

public class Main{

    public static void main(String[] arg){
        Scanner scan = new Scanner(new BufferedInputStream(System.in));
        int n;
        int count = 1;
        answer[0] = 1;
        while(scan.hasNextInt()){
            n = scan.nextInt();
            System.out.println("Case "+ count+++ ":" );
            checkAndOutput(n,1);
            System.out.println();
        }
        scan.close();
    }

    static int[] answer = new int[21];
    static int[] isUsed = new int[21];

    static void checkAndOutput(int n,int index) {//index为数组的序号
        int len = n+1;
        int last = n-1;
        if(index == n){
            for(int i=0; i != n ; i ++ ){
                if(i == last){
                    System.out.println(answer[i]);
                    return;
                }
                System.out.print(answer[i]+" ");
            }
        }
        for(int i = 2 ; i != len ; i ++ ){//数组序号为index的遍历
            if(isUsed[i] == 0){
                if(!isAPrimeInForty(i + answer[index-1])){
                    continue;
                }
                isUsed[i]=1;
                answer[index] = i;
                if(index==last){
                    if(!isAPrimeInForty(1+ answer[index])){//增加一步头尾相加判断
                        isUsed[i]=0;
                        continue;
                    }
                }
                checkAndOutput(n,index+1);
                isUsed[i]=0;
            }
        }
    }

    /*
     * 输入数字,作为序号传入
     * 判断是否质数
     * */
    static boolean isAPrimeInForty(int num){
        if(isPrimes[num]==1){
            return true;
        }
        return false;
    }

    /*
     * 生产序号为0-39的0,1序列
     * 1代表为质数
     * 0代表为非质数
     * */
    static int[] isPrimes = createPrimes();

    static int[] createPrimes(){
        int[] primes = new int[40];
        for(int i = 2 ; i != 40 ; i ++ ){
            if(isAPrime(i)){
                primes[i] = 1;
            }
        }
        return primes;
    }

    //判断是否质数
    static boolean isAPrime(int num){
        int len = (int) (Math.sqrt(num)+1);
        for(int i = 2 ; i != len ; i ++){
            if(num%i==0){
                return false;
            }
        }
        return true;
    }

    static void swap(int[] nums , int i , int j){
        int t;
        t = nums[i];
        nums[i] = nums[j];
        nums[j] = t;
    }
}
时间: 2024-11-06 09:34:05

HDOJ-ACM1016(JAVA) 字典序全排列,并剪枝的相关文章

java字典序全排列

import java.util.Arrays; /** *字典序全排列 *字符串的全排列 *比如单词"too" 它的全排列是"oot","oto","too" *1,从右端开始扫描,若出现前一个比后一个小,记录前一个的元素下表index *2,再找出index以后比该元素大的中的最小值的下标,(实现见 下面的getMin方法) *3,index以后的元素实现反转(实现 见下面的reverse方法) *结束条件:前一个都比后一

字典序全排列(java实现)

import java.util.Arrays; /** *字典序全排列 *字符串的全排列 *比如单词"too" 它的全排列是"oot","oto","too" *1,从右端开始扫描,若出现前一个比好一个小,记录前一个的元素下表index *2,再找出index以后比该元素大的中的最小值的下标,(实现见 下面的getMin方法) *3,index以后的元素实现反转(实现 见下面的reverse方法) *结束条件:前一个都比后一

分治法实现1-N的数字按字典序全排列组合 Java语言

package 分治法; import java.util.Arrays; /* * 将数字 1 - n进行全排列 按字典序从小到大输出 * 如 1 - 3 * 123 132 213 231 312 321 */ class GenerateP{ private int n; // 求 1-n所有数字的全排列 private final int maxn = 110;//最多可排列组合的长度 1-100 private boolean [] hashTable; private int []

HDOJ 5113 Black And White DFS+剪枝

DFS+剪枝... 在每次DFS前,当前棋盘的格子数量的一半小于一种颜色的数量时就剪掉 Black And White Time Limit: 2000/2000 MS (Java/Others)    Memory Limit: 512000/512000 K (Java/Others) Total Submission(s): 194    Accepted Submission(s): 50 Special Judge Problem Description In mathematics,

JAVA求解全排列

一,问题描述 给定一个字符串,求出该字符串的全排列. 比如:"abc"的全排列是:abc.acb.bac.bca.cab.cba 二,实现思路 采用递归的方式求解.每次先选定一个字符,然后进行“若干次”交换,求出在选定这个字符的条件下,所有的全排列,并把字符“复位”再交换回来.至此,一趟全排列完成.第二趟,选定下一个字符,然后进行“若干次”交换,求出在选定这个字符的条件下,所有的全排列,并把字符“复位”再交换回来...... 就类似于:(参考网上的解释如下:) 设R={r1,r2,..

HDOJ 1000 java

第一次做hdoj 试了好几次才通过 发现主类的名字必须是Main import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner= new Scanner(System.in); while(scanner.hasNext()) { int A=scanner.nextInt(); int B=scanner.nextInt(); System.out.p

字典序全排列

给出正整数n,则1~n这n个数可以构成n!种排列,把这些排列按照从小到大的顺序(字典顺序)列出,如n=3时,列出1 2 3,1 3 2,2 1 3,2 3 1,3 1 2,3 2 1 这6个排列. 字典序算法如下: 假设这n个数的某一个排列为 P: P1 P2 P3...Pj-1 Pj Pj+1...Pk-1 Pk Pk+1...Pn 1.从该序列的最右端开始向左找出第一个比与自己相邻的右边数小的数,记其下标为j,即j = max{i|Pi<pi+1}. 2.找出Pj右边比Pj大的最小数Pk.

ACM--大数阶乘--HDOJ 1042--N!--Java

HDOJ题目地址:传送门 N! Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others) Total Submission(s): 74952    Accepted Submission(s): 21832 Problem Description Given an integer N(0 ≤ N ≤ 10000), your task is to calculate N! Inp

全排列 字典序全排列

全排列递归的方法参考 leetcode 47 字典序算法:升序 参考https://www.jianshu.com/p/58ae30cf6bca 实现: 判断了是否相等 计算全排列的数量方法为 n!/ (m!*p!*...)   m,p为重复的数字的重复量 参考 https://blog.csdn.net/sinat_36215255/article/details/78197129 #include<iostream> #include<algorithm> #include&l