1. 程式人生 > >家喻戶曉的中藥店 (題解及一些素數打表方法)

家喻戶曉的中藥店 (題解及一些素數打表方法)

問題描述

long_xiao和const_hhh是一對恩愛的夫妻。

他們在京城經營著一家中藥店,夫妻二人醫術精湛、古道熱腸,雖然年過花甲,身體依然硬朗。更重要的是,他們的思維仍然十分活躍,不僅瞭解大家的要求,還能給他們許多驚喜。

除了治病救人,他們的中藥配方還有舒筋活絡,排毒養顏的功效。正因為如此,中藥店門庭若市,甚至有人不遠千里,慕名而來。

藥店裡藥材種類繁多,組成的配方也就非常多。為了提高服務質量,店裡的夥計灰來灰去將藥材和配方進行編號,灰來灰去可以通過配方的編號快速找到所需藥材的編號。

一天,店裡的夥計灰來灰去提議可以藉此機會來向大家普及一下數學知識,夫妻二人表示贊成,決定每週一在店門口的公告欄中釋出新的知識點。

這週一他們提供了一個簡單但是有趣的知識點:

“素數:一個大於1的自然數,除了1和它自身外,不能被其他自然數整除的數叫做素數“。

到了週末,灰來灰去為了檢驗大家對知識點的掌握情況,使得今天的配方都由三種藥材組成,所需的三種藥材的編號都為素數,且加起來等於配方的編號。因為會有多種情況出現,灰來灰去使得三個素數的乘積最大。

現在,藥店會給你開出一劑配方,編號為n。如果你能把他拆成3個素數的和或者告訴灰來灰去無法拆成3個素數的和,那麼灰來灰去就可以快速找到藥材,並免費贈送你一副他們的鎮店配方。

輸入描述

輸入第一行包含一個正整數T,代表有T次配方的詢問。

對於每組資料,輸入包含一個正整數n(1<=n<=10000),代表配方的編號。

輸出描述

對於每組資料,如果n不能寫成三個素數的和,輸出-1。
否則在一行從小到大輸出三個素數以及最大乘積。

樣例輸入

2
20
3

樣例輸出

2 7 11 154
-1
看到這個題的第一反應就是要打表,這裡說一下幾種素數打表的方法

  1. 普通方法
    時間複雜度為O(n^2)
void prime(int n)
{
	for (int i = 2; i < n; i++)
	{
		int j;
		for (j = 2; j <= i; j++)  
		{
			if (i%j==0) break;
		}
	}
}
  1. 普通方法改進——迴圈到sqrt(n)
    時間複雜度為O(n*sqrt(n))
void prime(int n)
{
	for (int i = 2; i < n; ++i)
	{
		int j;
		for (j = 2; j <= sqrt(i); ++j)  
		{
			if (i%j==0) break;
		}
	}
}

  1. 普通篩表——埃拉託斯特尼篩法
    所使用的原理是從2開始,將每個素數的各個倍數,標記成合數。一個素數的各個倍數,是一個差為此素數本身的等差數列。此為這個篩法和試除法不同的關鍵之處,後者是以素數來測試每個待測數能否被整除。時間複雜度為O(n log log n)
void isprime(int n, int prime[]) 
{
    int i;
    for(i = 0; i <= n; i++) 
        prime[i] = i;
    int j;
    for(j = 2; j < sqrt(n); j++)
     {
        int k;
        for(k = j + 1; k <= n; k++)
         {
            if(prime[k] != 0 && prime[k] % j == 0) 
            	prime[k] = 0;
        }
    }
} 
  1. 線性篩表——尤拉篩表
    時間複雜度降低到O(n)
void isprime()
{
   memset(visit, true, sizeof(visit));
   int num = 0;
   for (int i = 2; i <= n; ++i)
   {
       if (visit[i] == true)
       {
           num++;
           prime[num] = i;
       }
       for (int j = 1; ((j <= num) && (i * prime[j] <= n));  ++j)
       {
           visit[i * prime[j]] = false;
           if (i % prime[j] == 0) break; 
       }
           
   }
}

本題程式碼:

#include<stdio.h>
#include<math.h>
#include<string.h>
#define min(a,b)(a<b?a:b)
#define max(a,b)(a>b?a:b)

int prime[10000];
int num;
bool visit[10000];


void pr()
{
    memset(visit, true, sizeof(visit));
    num = 0;
    for (int i = 2; i <= 10000; ++i)
    {
        if (visit[i] == true)
        {
            num++;
            prime[num] = i;
        }
        for (int j = 1; ((j <= num) && (i * prime[j] <= 10000));  ++j)
        {
            visit[i * prime[j]] = false;
            if (i % prime[j] == 0) break; 
        }
            
    }
}

int main()
{
	pr();
	int t,n;
	scanf("%d",&t);
	while(t--)
	{	
		scanf("%d",&n);
		long long int Max=-1,m;
		int x,y,z,k;
		for(int i=0;i<num;i++)
		{
			for(int j = 0 ; j<num ; j++)
			{
				int k = n-prime[i]-prime[j];
				if(k >=0 && visit[k])
				{
					m=(long long int)prime[i]*prime[j]*k;
					if((m > Max)&&m)
					{	
						Max = m;
						x = min(prime[i] , min(prime[j] , k));
						z = max(prime[i] , max(prime[j] , k));
						y = n-x-z;
					}		
				}
			} 
		}
		if(Max == -1) 
			 printf("-1\n");
		else	
			printf("%d %d %d %lld\n",x,y,z,Max);
	}
	return 0;
}