PAT(Basic Level) Practice——1005 繼續(3n+1)猜想
原題目:
卡拉茲(Callatz)猜想已經在1001中給出了描述。在這個題目裡,情況稍微有些複雜。
當我們驗證卡拉茲猜想的時候,為了避免重複計算,可以記錄下遞推過程中遇到的每一個數。例如對n=3進行驗證的時候,我們需要計算3、5、8、4、2、1,則當我們對n=5、8、4、2進行驗證的時候,就可以直接判定卡拉茲猜想的真偽,而不需要重複計算,因為這4個數已經在驗證3的時候遇到過了,我們稱5、8、4、2是被3“覆蓋”的數。我們稱一個數列中的某個數n為“關鍵數”,如果n不能被數列中的其他數字所覆蓋。
現在給定一系列待驗證的數字,我們只需要驗證其中的幾個關鍵數,就可以不必再重複驗證餘下的數字。你的任務就是找出這些關鍵數字,並按從大到小的順序輸出它們。
輸入格式:每個測試輸入包含1個測試用例,第1行給出一個正整數K(<100),第2行給出K個互不相同的待驗證的正整數n(1<n<=100)的值,數字間用空格隔開。
輸出格式:每個測試用例的輸出佔一行,按從大到小的順序輸出關鍵數字。數字間用1個空格隔開,但一行中最後一個數字後沒有空格。
輸入樣例:
6
3 5 6 7 8 11
輸出樣例:
7 6
分析:
1.資料讀入較為簡單,開闢一個數組既可以。
2.找“關鍵數”,既可以看成由一個數經過“砍”,“砍”的過程中所得到的數能把所給數全部“覆蓋”,最後留下能“覆蓋”所有數的 “關鍵數”,列印結果即可。
思路
開闢一個二維陣列A[2][n],A[0][0-n]儲存讀取待驗證的數,A[1][0-n]儲存flag,初始flag標記為1,flag=1,沒有被“覆蓋”,即為“關鍵數”;flag=0,已經被“覆蓋”,不再判定。
一層FOR【for(i=0;i<n;i++)】迴圈,從陣列下標0開始迴圈整個陣列,掃描整個陣列的數,進行判定。
迴圈內的判定依據是A[1][i]處的flag為1,即沒有被“覆蓋”,否則繼續迴圈。
將當前A[0][i]的儲存到K,方便之後比較,之後開始進行“砍”(砍的時候有兩種情況,一種偶數的時候,一種是奇數的時候,分情況討論),再用一次FOR迴圈【for(j=0;j<n;j++)】對於每一個“砍”出來的數K與原陣列中的數進行比較,若有,則將其A[1][i]的值改為0,即已經被“覆蓋”,不再判定。比較的依據是:當前迴圈的A[1][j]的flag為1,即沒有被覆蓋, 同時 不判定當前計算的數 即:【j !=i】,再有,當前K值等於A[0][j] , 即:【A[1][j]==1&&j!=i&&A[0][j]==k】
整個陣列掃描完成,留下A[1][0-n]中為1的即為“關鍵數”,將關鍵數從陣列中取出存入另一個數組進行排序,或者直接進行對A[1][0-n]為1的排序,之後再列印結果。
反思:
複雜度有點兒高,或許存在更好的解法,有待考慮;
程式碼:
C語言版:
#include <stdio.h>
#include <string.h>
int main()
{
int n,i,j,k,m,s;
scanf("%d",&n);
int A[2][n],B[n];
for(i=0;i<n;i++)
{
scanf("%d",&A[0][i]);
//初始flag為1,即初始都為關鍵數
A[1][i]=1;
}
for(i=0;i<n;i++)
{
//對於flag為1進行判定,flag為0則不是關鍵數
if(A[1][i]==1)
{
k=A[0][i];
while(k!=1)
{
//偶數判定
if(k%2==0)
{
k=k/2;
for(j=0;j<n;j++)
{
//對於覆蓋的數,flag標記為0
if(A[1][j]==1&&j!=i&&A[0][j]==k)
{
A[1][j]=0;
}
}
}
else//奇數判定
{
k=(k*3+1)/2;
for(j=0;j<n;j++)
{
if(A[1][j]==1&&j!=i&&A[0][j]==k)
{
A[1][j]=0;
}
}
}
};
}
}
//取出flag為1的數
for(i=0,j=0;i<n;i++)
{
if(A[1][i]==1)
{
B[j]=A[0][i];
j++;
}
}
//排序,對於大量資料複雜度大,有待提高
for(k=0;k<j;k++)
{
for(m=k+1;m<j;m++)
{
if(B[k]<B[m])
{
s=B[k];
B[k]=B[m];
B[m]=s;
}
}
}
//列印結果,注意末尾不允許有空格
for(k=0;k<j;k++)
{
printf("%d",B[k]);
if(k!=j-1)
{
printf(" ");
}
}
return 0;
}