算法設計與分析——符號三角形問題(回溯法)

1、問題描述

下圖所示的三角形中,有14個「+「和14個「-」。2個同號下面是+,兩個異號下面是-。


    在通常狀況下,符號三角形的第一行有n個符號。符號三角形問題,要求對於給定的n,計算有多少個不一樣的符號三角形,使其所含的「+」和「-」相同。java

2、算法分析

用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));
  }
}

 3、算法效率

相關文章
相關標籤/搜索