
問題描述新生入學(xué)后圖書館將 N 本書擺在一條書架上。書的編號為 1,2,…,N。目前這些書的順序可能是亂的。管理員希望通過調(diào)整書的位置使書架最終變?yōu)榈?1 個位置放編號 1 的書第 2 個位置放編號 2 的書依次類推。書架前安裝了一臺軌道機械臂。每次操作時小藍可以選擇三個連續(xù)的位置機械臂會交換第一個位置和第三個位置上的書中間位置上的書保持不動。例如當(dāng)前書架順序為 1,2,3,4,5。選擇第 2 至第 4 個位置后編號為 2 和 4 的書會交換書架變?yōu)?1,4,3,2,5。現(xiàn)在給出書架上 N 本書的當(dāng)前順序請你計算至少需要進行多少次操作才能將書架恢復(fù)為 1,2,…,N 的順序。如果無論如何都無法完成輸出 ?1。輸入格式第一行包含一個整數(shù) N3≤N≤2000表示書的數(shù)量。第二行包含 N 個整數(shù) A1,A2,…,AN?表示書架當(dāng)前從左到右的順序。保證 A是 1~N 的排列。輸出格式輸出一個整數(shù)表示恢復(fù)書架順序所需的最少操作次數(shù)如果無法完成輸出 ?1。樣例說明小藍可以按下面的順序操作選擇第 3 到第 5 個位置書架變?yōu)?5,2,1,4,3選擇第 1 到第 3 個位置書架變?yōu)?1,2,5,4,3選擇第 3 到第 5 個位置書架變?yōu)?1,2,3,4,5。因此至少需要進行 3 次操作。代碼展示import java.util.Scanner; // 1:無需package // 2: 類名必須Main, 不可修改 public class Main { public static void main(String[] args) { Scanner scan new Scanner(System.in); //在此輸入您的代碼... int N scan.nextInt(); int[] arr new int[N1]; for(int i1;iN;i){ int num scan.nextInt(); arr[i] num; } //奇偶數(shù)位要分別對應(yīng)奇偶數(shù) boolean b true; for(int i1;iN;i){ if(i%20){ int o i;//偶數(shù)位 if(arr[o]%2!0){ b false; break; } }else{ int j i;//奇數(shù)位 if(arr[j]%2!1){ b false; break; } } } if(bfalse){ System.out.println(-1); return; } //從第一位數(shù)字開始放回正確的位置 int count 0; for(int i1;iN;i){ while(arr[i]!i){ int p; for(pi;pN;p){ if(arr[p]i){ break; } } int t arr[p]; arr[p] arr[p-2]; arr[p-2] t; count; } } System.out.println(count); scan.close(); } }要點拆解經(jīng)觀察不難得知只有滿足奇數(shù)位置的編號必須為奇數(shù)偶數(shù)位置的編號必須為偶數(shù)才能恢復(fù)順序否則輸出-1。這里要注意分奇偶的寫法最開始我寫的是//奇偶數(shù)位要分別對應(yīng)奇偶數(shù) boolean b true; for(int i1;iN;i){ int j 2*i-1; int o 2*i; if(arr[j]%2!1||arr[o]%2!0) b false; } if(bfalse){ System.out.println(-1); }但這一定是錯的這樣寫顯然沒有考慮數(shù)組越界的情況。比如當(dāng) i 取 n 的時候arr [ j ] 和arr [ o ] 就會越界而報錯。于是我改成了下面這個版本//奇偶數(shù)位要分別對應(yīng)奇偶數(shù) boolean b true; for(int i1;iN;i){ int o,j;//聲明全局變量 if(i%20){ o i;//偶數(shù)位 }else{ j i;//奇數(shù)位 } if(arr[o]%2!0||arr[j]%2!1){ b false; } } if(bfalse){ System.out.println(-1); }不難發(fā)現(xiàn)這里的邏輯依然是錯的第一點每次循環(huán)經(jīng)過if - else 邏輯時只能選擇其中一條而這就必然導(dǎo)致總有 o 或者 j 沒有值而Java 要求局部全局變量使用前必須被賦值此處埋下一個伏筆。 比如 i 是奇數(shù)的時候執(zhí)行 else給ji但是o完全沒賦值后面你寫arr[o]就報錯。同理i 是偶數(shù)的時候j沒有賦值。第二點每一輪循環(huán)i只是單個位置不要試圖同時處理 o、j 兩個位置必須分開寫因而才有了第三個版本的出現(xiàn)//奇偶數(shù)位要分別對應(yīng)奇偶數(shù) boolean b true; for(int i1;iN;i){ if(i%20){ int o i;//偶數(shù)位 if(arr[o]%2!0){ b false; } }else{ int j i;//奇數(shù)位 if(arr[j]%2!1){ b false; } } } if(bfalse){ System.out.println(-1); }這個版本終于沒有邏輯問題了但仍然有瑕疵聰明的你發(fā)現(xiàn)了嗎問題在于發(fā)現(xiàn)無解打印-1后一定要return終止 main 方法不然后面還會繼續(xù)輸出 count。最終版本終于出來了//奇偶數(shù)位要分別對應(yīng)奇偶數(shù) boolean b true; for(int i1;iN;i){ if(i%20){ int o i;//偶數(shù)位 if(arr[o]%2!0){ b false; } }else{ int j i;//奇數(shù)位 if(arr[j]%2!1){ b false; } } } if(bfalse){ System.out.println(-1); return; }順序重排的邏輯梳理從第一個位置開始按順序依次往后判斷編號是否到達自己對應(yīng)的位置。如果不能對應(yīng)先找到數(shù)字1目前的位置每次向左退兩位直到到達第一個位置。后續(xù)以此類推...局部變量p的使用還記得前面埋的伏筆嗎你可能會問這里的p不也沒有初始化嗎為什么在這里就能使用p作為局部變量呢其根本原因并不是變量有沒有在一開始直接賦值而是在于在讀取之前有沒有被賦值如果代碼路徑保證一定能給 p 賦值那就沒問題 比如這里int p;聲明變量此時 p 無值。緊接著執(zhí)行for(p i; …)→for 循環(huán)的初始化部分pi給 p 賦值了對本題中while內(nèi)部for循環(huán)的理解1p應(yīng)該從i開始遍歷因為前面的1 ~ i - 1個元素已經(jīng)排列好無需在重復(fù)循環(huán)2for(pi; pN; p)里面的break會立刻跳出這個 for 循環(huán)此時 p 就停在找到的那個下標不會再繼續(xù)增加了。break的作用終止當(dāng)前這一層 for 循環(huán)保留 p 當(dāng)前的值直接跳到 for 循環(huán)后面的代碼。因此不必擔(dān)心for循環(huán)內(nèi)部沒有保存目標的p,break出來后符合條件的p就會自己凍結(jié)從而能用于后續(xù)的代碼。最后強調(diào)一下引用類型值傳遞與基本類型值傳遞的區(qū)別引用類型傳遞的是該對象地址值的副本兩個變量保存的是同一對象的地址指向的是同一個對象修改其中任意一個變量的數(shù)據(jù)都會改變兩個變量的值而基本類型傳遞的是數(shù)據(jù)副本在副本修改數(shù)據(jù)并不會影響原先變量的值。