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


一、問題描述

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


    在一般情況下,符號三角形的第一行有n個符號。符號三角形問題,要求對於給定的n,計算有多少個不同的符號三角形,使其所含的“+”和“-”相同。

二、算法分析

用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、公差為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為奇數時,顯然不存在“+”和“-”個數相同的符號三角形。

這里舉一個第1行是4個字符的符號三角形的子集樹

 

對於這道題只要確定第1行的n個符號其實最后畫出的符號三角形一定是唯一的,在求第1行n個符號的時,每次確定一個符號時,由該字符與之前已有的字符能組合出一個符號三角形,該符號三角形為最終要求的符號三角形的子集,新確定的符號可以作為右邊如圖紅色箭頭那樣,將路徑上的符號得到。

 

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

 三、算法效率

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM