【算法】HDOJ-1016 Prime Ring Problem

来源:互联网 发布:网络7层协议 编辑:程序博客网 时间:2024/05/18 01:49


PrimeRing Problem

TimeLimit: 4000/2000 MS (Java/Others)    MemoryLimit: 65536/32768 K (Java/Others)
Total Submission(s):16943    Accepted Submission(s): 7699


ProblemDescription

Aring is compose of n circles as shown in diagram. Put natural number1, 2, ..., n into each circle separately, and the sum of numbers intwo adjacent circles should be a prime.

Note: the number offirst circle should always be 1.

 


Input

n(0 < n < 20).

 


Output

Theoutput format is shown as sample below. Each row represents a seriesof circle numbers in the ring beginning from 1 clockwisely andanticlockwisely. The order of numbers must satisfy the aboverequirements. Print solutions in lexicographical order.

Youare to write a program that completes above process.

Print ablank line after each case.

 


SampleInput

68

 


SampleOutput

Case 1:1 4 3 2 5 61 6 5 2 3 4Case 2:1 2 3 8 5 6 7 41 2 5 8 3 4 7 61 4 7 6 5 8 3 21 6 7 4 3 8 5 2


My code:

//1016_2.cpp

//最初的想法,遍历出每一种情况,判断相邻数之和是否为素数。执行效率非常低,运行超时

#include<iostream>using namespace std;int main(){    void case_bool_cout(int count,int n,int num[]);    int n,count,num[100]={0};    num[0]=1;    while(cin>>n){        count=1;        case_bool_cout(count,n,num);    }    return 0;}void case_bool_cout(int count,int n,int num[100]){    bool repeat(int,int num[]);    int i,j;    bool case_prime(int n,int num[]);    if(count==n){   //执行到最后,产生了一种情况        if( repeat(n,num) ) ;    //判断数组中是否有重复的数        else if( case_prime(n,num) ){   //没有重复的,接着判断,是否符合 相邻两数和为素数的 条件            for(j=0;j<n;j++)  //都符合,输出这种情况                cout<<num[j]<<' ';            cout<<endl;                }    }    else{    //情况产生中        for(j=2;j<=n;j++){ //循环该位上可能的数              num[count]=j;            case_bool_cout(count+1,n,num);  //count表示循环到哪一位了        }    }        }bool case_prime(int n,int num[100])   //判断该种情况是否符合题目要求,即相邻两数和为素数。{    int s,i;    bool num_prime(int n);  //判断一个整数是否为素数    bool f;    f=true;    for(i=0;i<n;i++){       //循环判断这种情况下相邻两数和是否为素数        if(i==n-1) s=num[0]+num[i];            else s=num[i]+num[i+1];        if( ! num_prime(s) ) {f=false; break;}  //如果有一个不是素数,表示这种情况不符合要求,跳出循环    }    return f;}bool num_prime(int n){    bool f=true;    int i;    for(i=2;i<n;i++)        if(n%i==0) {f=false;break;}    return f;}bool repeat(int n,int num[100]){    int i,j;    for(i=0;i<n-1;i++)        for(j=i+1;j<n;j++)            if(num[i]==num[j])                 return true;    return false;}

//1016_3.cpp

//提交通过,但是效率不高,且占用空间大。

//Pro.ID Exe.Time Exe.Memory CodeLen. Language

//1016 859MS 340K 2039B C++

//经过改进,运用了临接矩阵表列出所有情况,储存在二维数组中,之后就是从中搜索出符合条件的情况,输出。但是运行时间还是较长,未经过优化。


#include<iostream>usingnamespace std;intprime_table[19][19] = {{1,1,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0},{1,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0},{0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0},{1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1},{0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0},{1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0},{0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0},{0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0},{0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0},{1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1},{0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0},{1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1},{0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0},{0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0},{0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0},{1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0},{0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0},{1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1},{0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0}};intcount=0;intmain(){voidergod(int ,int [19],int );intdigit=1,n,queen[19]={0};while(cin>>n){count++;digit=1;queen[0]=1; //首先给队列里第一个数赋值 1cout<<"Case"<<count<<':'<<endl;if(n==1)cout<<endl;else{ergod(digit,queen,n);//遍历所有情况,如果符合条件,输出。cout<<endl;}}return0;}voidergod(int digit,int queen[19],int n) //确定第digit位的数{boolf; //true表示不重复inti,j;if(digit< n) //位数还没判断到最后一位,继续本位数的遍历。for(j=0;j<n;j++){ //j代表与本位相邻的下一位数f=true;if(prime_table[queen[digit-1]-1][j]== 1){ //如果刚才那位的数与下一位数和是质数,继续执行。for(i=0;i<digit;i++) //循环数组,判断该数与之前各位数有没有重复if(j+1==queen[i]){f=false;break;}if(f==true){ //没有重复queen[digit]=j+1;ergod(digit+1,queen,n);}}}else{ //到最后了,输出这种情况if(prime_table[0][queen[n-1]-1] ==1 ){for(j=0;j<n;j++){if(j==n-1)cout<<queen[j];elsecout<<queen[j]<<'';}cout<<endl;}}}

//1016_4.cpp

//经过了优化

//Pro.ID Exe.Time Exe.Memory CodeLen. Language

//1016 296MS 252K 1742B G++

//优化:

//将输入输出头文件iostream换成了stdio.h

//将所有输入输出的cincout换成scanfprintf,因为这样输入输出效率很高

//删掉了所有注释

//这样运行时间一下子降低了500+MS,代码长度也降低了一点。


#include<stdio.h>usingnamespace std;intprime_table[19][19] = {{1,1,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0},{1,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0},{0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0},{1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1},{0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0},{1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0},{0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0},{0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0},{0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0},{1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1},{0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0},{1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1},{0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0},{0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0},{0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0},{1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0},{0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0},{1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1},{0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0}};intcount=0;intmain(){voidergod(int ,int [19],int );intdigit=1,n,queen[19]={0};while(scanf("%d",&n)!=EOF){count++;digit=1;queen[0]=1;printf("Case%d:\n",count);if(n==1)printf("\n");else{ergod(digit,queen,n);printf("\n");}}return0;}voidergod(int digit,int queen[19],int n){boolf;inti,j;if(digit< n)for(j=0;j<n;j++){f=true;if(prime_table[queen[digit-1]-1][j]== 1){for(i=0;i<digit;i++)if(j+1==queen[i]){f=false;break;}if(f==true){queen[digit]=j+1;ergod(digit+1,queen,n);}}}else{if(prime_table[0][queen[n-1]-1] ==1 ){for(j=0;j<n;j++){if(j==n-1)printf("%d",queen[j]);elseprintf("%d",queen[j]);}printf("\n");}}}




算法分析:

    这道题我开始的想法很简单,就是枚举一遍,其实后面的也是遍历出所有适合的情况,只不过开始的算法写的很笨拙,以致效率很低。后来经过老师指点,看了《算法导论》图论那方面的知识,加上请教学长,最后用临接矩阵表解决了这个问题。临接矩阵表是我直接列出,放到代码里,这样不用再写代码去求矩阵表,相当于用空间复杂度去换时间复杂度,变相的降低了难度。但是如果n给的范围很大,我就不可能把所有情况都列到代码里,所以我想后面还可以改进,将求临接矩阵表的函数写出来。关于这方面,老师今天有过指导,可以用筛法求矩阵,先把范围内的素数列出来,然后写个循环,用这个素数减去循环变量,求出另一半,而且只需计算到一半即可。

      而后面的算法,也可以试试用分支定界法求解。这题里我用的是回溯法,回溯法是深度优先算法,效率低,空间占用少。而分支定界法是广度优先算法,效率高,空间占用多。

      这个问题放到后面的学习中再深入研究,这道题就先这样。

      这里给出关于分支定界法的相关资料,便于以后深入:


百度百科-分支定界法:http://baike.baidu.com/view/587272.htm

百度百科-分支限界法:http://baike.baidu.com/view/587272.htm

分支定界(branchand bound) 算法:http://bbs.ednchina.com/BLOG_ARTICLE_213865.HTM




原创粉丝点击