1. 程式人生 > >符號三角形問題—回溯演算法—java實現

符號三角形問題—回溯演算法—java實現

問題描述:

    下圖是由14個“+”和14個“-”組成的符號三角形。2個同號下面都是“+”,2個異號下面都是“-”:

                    

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

解題思路:

    1、不斷改變第一行的每個符號,搜尋符合條件的解,可以使用遞歸回溯。

        為了便於運算,設+ 為0,- 為1,這樣可以使用異或運算子表示符號三角形的關係:
            ++為+即0^0=0, --為+即1^1=0, +-為-即0^1=1, -+為-即1^0=1
 

    2、因為兩種符號個數相同,可以對題解樹剪枝

        當所有符號總數為奇數時無解,當某種符號超過總數一半時無解

演算法設計:

        

     在符號三角形的第一行的前i個符號x[1:i]確定後,就確定了一個由i*(i+1)/2個符號組成的符號三角形。

    下一步確定了x[1:i]的值後,只要在前面確定的符號三角形的右邊加一條邊,就可以擴充套件為x[1:i]所對應的符號三角形。

    在回溯法搜尋過程中可用當前符號三角形所包含的“+”號個數與“-”號個數均不超過n*(n-1)/4作為可行性約束,用於剪去不滿足約束條件的子樹。

程式碼:

    需要的資料:

        static int n;//第一行的符號個數
	static int half;//n*(n+1)/4
	static int count;//當前“+”或者“-”的個數
	static int[][] p;//符號三角形矩陣
	static long sum;//已找到的符號三角形的個數

    初始化及計算:

public static float Compute(int nn) {
		n = nn;
		count = 0;
		sum = 0;
		half = (n*(n+1))>>1;
		if((half>>1)<<1 != half) {
			return 0;
		}
		half = half>>1;
		p = new int[n+1][n+1];
		backtrack(1);
		
		return sum;
	}

    回溯演算法1(僅用於計算行數或者列數為n的符號三角形的總數,沒有顯示符號三角形的功能,但是是核心程式碼):

public static void backtrack01(int t) {
		if(count>half || (t*(t-1)>>1 - count)>half) {//對題解樹的剪枝
			return;
		}
		if(t>n) {
			sum++;//符號三角形的總數目+1
		}
		else {
			//每個位置都有兩種情況0,1
			for(int i = 0;i<2;i++) {
				p[1][t] = i;
				count += i;//對"-"個數進行技術,為了進行剪枝操作
				
				//接下來繪製其餘的n-1行
				for(int j = 2;j<=t;j++) {
					//通過異或的方式求其餘行數的放置方式
					p[j][t-j+1] = p[j-1][t-j+1]^p[j-1][t-j+2];
					count += p[j][t-j+1];	
				}
				backtrack01(t+1);
				
				//恢復現場
				for(int j = 2;j<=t;j++) {
					count -= p[j][t-j+1];
				}
				count -= i;
			}
		}
	}

    回溯演算法2(具備列印符號三角形的功能):

public static void backtrack(int t) {
		if((count>half)||((t*(t-1)>>1-count)>half)) //對題解樹的剪枝
			return;
		if(t>n) {
			sum++;
			//列印符號三角形
			for(int i =1;i<=n;i++) {
				for(int k = 1;k<i;k++) {
					System.out.print(" ");
				}
				for(int j =1;j<=n;j++) {
					if(p[i][j] == 0 && j<=n-i+1) {
						System.out.print("+" + " ");
					}
					else if(p[i][j] == 1 && j<=n-i+1) {
						System.out.print("-" + " ");
					}
					else {
						System.out.print("  ");
					}
				}
				System.out.println();
			}
			System.out.println();
		}
		else {
			//每個位置都有兩種情況0,1
			for(int i =0;i<2;i++) {
				p[1][t] = i;
				count += i;//計算“-”的個數
				
				//接下來繪製其餘的n-1行
				for(int j = 2;j<=t;j++) {
					//通過異或的方式求其餘行數的放置方式
					p[j][t-j+1] = p[j-1][t-j+1]^p[j-1][t-j+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;
				
			}
		}
	}

完整程式碼:

package tri;

public class TRAN {

	static int n;//第一行的符號個數
	static int half;//n*(n+1)/4
	static int count;//當前“+”或者“-”的個數
	static int[][] p;//符號三角形矩陣
	static long sum;//已找到的符號三角形的個數
	
	public static float Compute(int nn) {
		n = nn;
		count = 0;
		sum = 0;
		half = (n*(n+1))>>1;
		if((half>>1)<<1 != half) {
			return 0;
		}
		half = half>>1;
		p = new int[n+1][n+1];
		backtrack(1);
		
		return sum;
	}
	
	/**
	 * 演算法1
	 * @param t
	 */
	/*
	public static void backtrack01(int t) {
		if(count>half || (t*(t-1)>>1 - count)>half) {//對題解樹的剪枝
			return;
		}
		if(t>n) {
			sum++;//符號三角形的總數目+1
		}
		else {
			//每個位置都有兩種情況0,1
			for(int i = 0;i<2;i++) {
				p[1][t] = i;
				count += i;//對"-"個數進行技術,為了進行剪枝操作
				
				//接下來繪製其餘的n-1行
				for(int j = 2;j<=t;j++) {
					//通過異或的方式求其餘行數的放置方式
					p[j][t-j+1] = p[j-1][t-j+1]^p[j-1][t-j+2];
					count += p[j][t-j+1];	
				}
				backtrack01(t+1);
				
				//恢復現場
				for(int j = 2;j<=t;j++) {
					count -= p[j][t-j+1];
				}
				count -= i;
			}
		}
	}
	*/
	
	public static void backtrack(int t) {
		if((count>half)||((t*(t-1)>>1-count)>half)) //對題解樹的剪枝
			return;
		if(t>n) {
			sum++;
			//列印符號三角形
			for(int i =1;i<=n;i++) {
				for(int k = 1;k<i;k++) {
					System.out.print(" ");
				}
				for(int j =1;j<=n;j++) {
					if(p[i][j] == 0 && j<=n-i+1) {
						System.out.print("+" + " ");
					}
					else if(p[i][j] == 1 && j<=n-i+1) {
						System.out.print("-" + " ");
					}
					else {
						System.out.print("  ");
					}
				}
				System.out.println();
			}
			System.out.println();
		}
		else {
			//每個位置都有兩種情況0,1
			for(int i =0;i<2;i++) {
				p[1][t] = i;
				count += i;//計算“-”的個數
				
				//接下來繪製其餘的n-1行
				for(int j = 2;j<=t;j++) {
					//通過異或的方式求其餘行數的放置方式
					p[j][t-j+1] = p[j-1][t-j+1]^p[j-1][t-j+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) {
		// TODO Auto-generated method stub
		float SUM = Compute(4);	
		System.out.print("總數: " + SUM);
	}

}

以行數為4的符號三角形為例,顯示輸出如下:



PS:本人水平有限,請多指教。吐舌頭


相關推薦

符號三角形問題—回溯演算法java實現

問題描述:    下圖是由14個“+”和14個“-”組成的符號三角形。2個同號下面都是“+”,2個異號下面都是“-”:                        符號三角形第一行有n個符號,符號三角形問題要求對於給定的n,計算有多少個不同的符號三角形,使其所含的“+”和“

0-1揹包問題—回溯演算法java實現

0-1揹包問題 【問題描述】 有n種可選物品1,…,n ,放入容量為c的揹包內,使裝入的物品具有最大效益。 表示 n :物品個數 c :揹包容量 p1,p2, …, pn:個體物品效益值 w1,w2, …,wn:個體物品容量 【問題解析】 0-1揹包問題的解指:物品1,…,n的一種放

演算法java實現--回溯法--旅行售貨員問題--排列樹

旅行售貨員問題的java實現(回溯法--排列樹) 具體問題描述以及C/C++實現參見網址 http://blog.csdn.net/liufeng_king/article/details/8890603 /** * 旅行售貨員問題--回溯法 * @author

基本排序演算法-java實現

最近重新學習了排序演算法,之前每次看完當時理解了,但是過一段時間就又忘了,尤其是程式碼,如果放一段時間有很多base case不知道怎麼寫了,所以還是應該詳細的解讀一下再不斷了敲程式碼才能理解比較深刻。 1.氣泡排序(bubble sort) 氣泡排序是一種簡單的排序演算法。其基本思

編輯距離演算法Java實現

/** * 計算編輯距離Edit Distance * if i == 0 且 j == 0,edit(i, j) = 0 * if i == 0 且 j > 0,edit(i, j) = j * if i > 0 且j == 0,edit(i,

分散式ID 雪花演算法JAVA實現

少年不想寫,來吧:https://github.com/singgel/SnowFlake snowflake的結構如下(每部分用-分開): 概述 分散式系統中,有一些需要使用全域性唯一ID的場景,這種時候為了防止ID衝突可以使用36位的UUID,但是UUID有一些缺點,首先他相對比

氣泡排序演算法java實現

package algorithm; /** * 氣泡排序演算法 * @author su * */ public class BubbleSort { public static void main(String[] args) { int[] a = {6,2,5,4,7,1,

推特雪花演算法 java實現

package twiter.snowflake; /** * twitter的snowflake演算法 -- java實現 */ public class SnowFlake { /** * 起始的時間戳 */ private final static long

最小生成樹Prim演算法java實現

package prim; import java.util.*; public class PrimTest { public static void main(String[] args) { //互動輸入圖的鄰接矩陣表示,為方便測試,直接給定了鄰接矩陣值 // System

Dijkstra演算法-Java實現

給定n個城市,並建立一個n*n的距離矩陣來存放兩兩城市之間的距離,當兩個城市之間不能直達時,將距離記為無窮大。對矩陣進行初始化: for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) {

反轉連結串列演算法Java實現

之前遇到反轉連結串列的演算法,比較晦澀難解,但其實挺簡單的。 目標:將一個順序連結串列反轉。 思路:用三個輔助節點,每次實現一個節點的指向反轉,即他的後繼變為他的前驅。           三個輔助節點: p  q  r&n

【資料結構與演算法-java實現】二 複雜度分析(下):最好、最壞、平均、均攤時間複雜度的概念

上一篇文章學習了:如何分析、統計演算法的執行效率和資源消耗? 點選連結檢視上一篇文章:複雜度分析上 今天的文章學習以下內容: 最好情況時間複雜度 最壞情況時間複雜度 平均情況時間複雜度 均攤時間複雜度 1、最好與最壞情況時間複雜度 我們首先

6種排序演算法java實現

6種排序演算法 氣泡排序 選擇排序 插入排序 計數排序 快速排序 歸併排序 1)氣泡排序       相鄰的兩個數字比較排序,先將最大的交換到最後面,然後重複。 程式碼實現 2)選擇排序       從第一個位置開始,用某個位置依次與後邊所有元

連結串列排序演算法java實現(連結串列的快速排序、插入排序、歸併排序)

難易程度:★★ 重要性:★★★      連結串列的排序相對陣列的排序更為複雜些,也是考察求職者是否真正理解了排序演算法(而不是“死記硬背”) 連結串列的插入排序 public class LinkedInsertSort { static cla

經典排序演算法java 實現

排序演算法的好壞對於效率的影響十分顯著。好的排序演算法排序100萬個整數可能只需要一秒(不考慮硬體因素),不好的排序演算法可能需要一個小時甚至幾個小時。 常見的排序演算法有氣泡排序、插入排序、堆排序、快速排序等,這些排序都屬於基於比較的排序,因此這些演算法的時間

Dijkstra演算法 java實現

import java.util.HashMap; import java.util.HashSet; import java.util.Map.Entry; /** * * Dijkstra演算法 * 適用範圍:沒有權值為負數的邊 * */ // no ne

KMP演算法java實現

package algorithm; public class KmpSearch { public static void main(String[] args) { String s1 = "ABABCABAA"; char[] pattern = s1.to

常見排序演算法Java實現

目前最經典的排序演算法要屬:氣泡排序,快速排序,簡單插入排序,希爾排序,簡單選擇排序,堆排序,二路歸併排序,多路鬼 並排序,計數排序,桶排序,基數排序。以下就是這些常見演算法的Java實現,有興趣的可以自行實現。現在我們就來一個個分析 介紹一下各自的基礎思想和實現。講解這些演

有權最短路徑問題:狄克斯特拉(Dijkstra)演算法 & Java 實現

一、有權圖 之前我們知道,在無權重的圖中,求兩個頂點之間的最短路徑,可以使用 廣度優先搜尋 演算法。但是,當邊存在權重(也可以理解為路程的長度)時,廣度優先搜尋不再適用。 針對有權圖中的兩點間最短路徑,目前主要有 狄克斯特拉演算法 和 貝爾曼福德演算法 兩種解決

MD5演算法java實現原始碼

流程 處理需要加密的字串: 以byte[]的形式獲取,此時,每個字元為一個位元組,佔8位 當此時獲得的位元組流對512位,也就是64個位元組取餘,結果不為448,即56個位元組時,使用1000……0的二進位制位進行補位,直到取餘結果為448,;而當結果為448時