下圖所示的三角形中,有14個「+「和14個「-」。2個同號下面是+,兩個異號下面是-。
在通常狀況下,符號三角形的第一行有n個符號。符號三角形問題,要求對於給定的n,計算有多少個不一樣的符號三角形,使其所含的「+」和「-」相同。java
用n元組x[1:n]表示符號三角形的第一行的n個符號,當x[i]等於1時,表示符號三角形的第一行的第i個符號爲「+」;當x[i]等於0時,表示符號三角形的第一行的第i個符號爲「-」;1<=i<=n。因爲x[i]是2值的。因此在用回溯法解符號三角形問題時,能夠用一棵徹底二叉樹來表示其解空間。在符號三角形的第一行的前i個符號x[1:i]肯定後,就肯定了一個有i*(i+1)/2個符號組成的符號三角形。算法
(i*(i+1)/2來自首項爲一、公差爲1的等差數列的求和公式)數組
下一步肯定x[i+1]的值後,只要在前面已肯定的符號三角形的右邊加一條邊,就能夠拓展爲x[1:i+1]所對應的符號三角形。最終由x[1:n]所肯定的符號三角形包含的「+」個數與「-」同爲n(n+1)/4(n(n+1)/2的一半,也就是一半的符號)。所以,在回溯可將「+」、「-」個數均 不超過n(n+1)/4爲約束條件。同時,對於給定的n當n(n+1)/2爲奇數時,顯然不存在「+」和「-」個數相同的符號三角形。
spa
這裏舉一個第1行是4個字符的符號三角形的子集樹code
對於這道題只要肯定第1行的n個符號其實最後畫出的符號三角形必定是惟一的,在求第1行n個符號的時,每次肯定一個符號時,由該字符與以前已有的字符能組合出一個符號三角形,該符號三角形爲最終要求的符號三角形的子集,新肯定的符號能夠做爲右邊如圖紅色箭頭那樣,將路徑上的符號獲得。blog
import java.io.*; import java.util.*; public class Triangles { public static int n, half, count;// 第一行的符號個數n,當前「+」個數count, public static int[][] p;// 符號三角形矩陣 public static long sum;// 符合條件的符號三角形個數 public static long computs(int nn) { n = nn; count = 0; sum = 0; half = n * (n + 1) / 2; if (half % 2 == 1)// 無解的判斷:n*(n+1)/2爲奇數 { return 0; } half = half / 2; p = new int[n + 1][n + 1]; for (int i = 0; i < n; i++)// 數組初值 { for (int j = 0; j < n; j++) { p[i][j] = 0; } } backtrack(1); return sum; } public static void backtrack(int t) { if ((count > half) || (t * (t - 1) / 2 - count > half)) return;// 若符號統計未超過半數,而且另外一種符號也未超過半數 if (t > n) { sum++; } // 當i>n時,算法搜索至葉節點,獲得一個新的「+」個數與「—」個數相同的符號三角形,當前已找到的符號三角形數sum增1. else { for (int i = 0; i < 2; i++) { p[1][t] = i; count += i; for (int j = 2; j <= t; j++) { if (p[j - 1][t - j + 1] == p[j - 1][t - j + 2]) { p[j][t - j + 1] = 1; // 2個同號下面都是「+」 } else { p[j][t - j + 1] = 0; // 2個異號下面都是「-」 } count += p[j][t - j + 1]; } backtrack(t + 1); for (int j = 2; j <= t; j++) { // 回溯時取消上一次的賦值 count -= p[j][t - j + 1]; } count -= i; } } } public static void main(String[] args) { System.out.println("請輸入第一行符號值:"); Scanner read = new Scanner(System.in); int n = read.nextInt(); System.out.println("個數:" + computs(n)); } }