藍橋杯 方格填數的java實現
博主今年大三了,最近報名了一個藍橋杯的個人項的java組。所以博主最近在刷題,且博主會把一些自認為比較好的題目拿出來跟大家分享一下(發出來的程式碼都是博主自己敲的,且都正確,這個大家就放心了)。
問題描述:
問題分析:
這個問題我是採用深度優先遍歷來解決的。每次填入一個數字,確保符合相鄰的數字在方格子中不相鄰。當格子填完的時候就是一種情況(注意數字不能重複)。
資料域:
1. private static int[] visited;
用來儲存資料0-9的狀態,0表示未被訪問,1表示已經被訪問過了。
2. private static int[][] dir= {{-1,-1},{-1,0},{0,-1},{-1,1}};
方向陣列用來表示被監測點的四周的點。
比如原陣列的座標為(x,y),經過 dx=x+dir[0][0]; dy=y+dir[0][1]的操作後,座標(dx,dy)就在原先座標的左上角。
可以用一個for迴圈把該陣列的八個方向都給遍歷了(雖然實際上並不需要遍歷這麼多的點)。
3.private static int[][] save;
儲存方格子裡面的數字(0-9,且數字不能重複)。
4.private static int sum;
用來表示符合的個數,沒出現一個符合的情況執行sum++操作。
注意:由於這是在java裡面,主函式是static型別的,所以所有的資料和方法都必須是static型別的(java裡面不允許靜態類訪問非靜態的資料和方法)。
下面是處理這幾個問題的方法(c以及c++裡面叫函式,但java裡面都是統一叫方法)。
1.資料的初始化
private static void init() { visited=new int[10]; for(int i=0;i<10;i++) { visited[i]=0;//0表示尚未訪問,1表示已經被訪問過了 } sum=0; save=new int[3][4]; for(int i=0;i<3;i++) { for(int j=0;j<4;j++) { save[i][j]=0; } } save[0][0]=-10; save[2][3]=-10; }
這裡面要注意一點:事實上,(0,0)和(2,3)這兩點並不在不在方格子裡面,但如果把(0,0)點所在的資料賦值為0且(0,1)的資料賦值為1,那麼他們兩個不滿足相鄰數字子在方格子裡面不相鄰的情況,從規定上來說,不能給(0,1)點賦值1.而實際上點(0,0)壓根不在方格的範圍之內,也就是說點(0,0)不應該參與計算。所以為了避免這種情況我們可以把這兩點的資料誇張一些,避免偶然性。
2.判斷資料是否越界
private static boolean border(int x,int y) {
if(x>=0&&x<3&&y>=0&&y<4) {
return true;
}
return false;
}
這裡面我們可以看到由於博主的偷懶原本不在方格內的這兩點博主並沒有排除掉,所以在上面賦值的時候要把那兩點的資料寫的大一點,避免偶然性。
3.判斷是否滿足相鄰數字在方格中不相鄰的情況
private static boolean check(int x,int y,int k) {
int dx,dy;
for(int i=0;i<4;i++) {
dx=x+dir[i][0];
dy=y+dir[i][1];
if(border(dx,dy)==true&&Math.abs(k-save[dx][dy])==1) {
return false;
}
}
return true;
}
x,y代表要監測點的位置,k代表要填入監測點的數字(如果滿足填入該數字,不滿足的話選取另外一個沒有被訪問過的數字)。
事實上並不需要遍歷所有方向,只需遍歷幾個方向就夠了。
4.dfs(int x,int y)
private static void dfs(int x,int y) {
if(x==2&&y==3) {
sum++;
}
else {
for(int i=0;i<10;i++) {
if(visited[i]==0&&check(x,y,i)==true) {//該元素未被訪問
save[x][y]=i;
visited[i]=1;
if(y<3) {
dfs(x,y+1);
}
else {
dfs(x+1,0);
}
visited[i]=0;
}
}
}
這就是一個簡單的深度搜索問題,裡面用到了遞迴和回溯的方法,很常見,就不多說了。需要注意的是遞迴的時候y<3(我當時寫成了y<4,結果編譯顯示陣列越界,傻fufu的)。
最終答案:1580