圖論+回溯解QQ一筆畫紅包
[春節整活]
QQ的一筆畫紅包有幾個特性:
1.最大為5×5的點陣,所以可以把每個點從左到右,從上到下標為1-25號點
2.每兩個點只能存在一條線
3.線可以被蓋住(例如連接2-1-3,2-1的線會被後來的1-3的連接線蓋住),對肉眼觀察很不利,但是對代碼來說沒有影響
解題思路:
1.對於線較多的點陣,可以使用鄰接矩陣來畫無向無權圖(線較少可以使用鄰接表),由於最大只有25個點,不必要考慮內存開銷,所以直接使用鄰接矩陣了
2.若某個節點所連接的線數為奇數,即為起點/終點,為偶數即為經過的點。若所有節點所連接的線數都為偶數,即首尾相連,任意點都可為起點/終點
3.使用回溯算法,找出將全部線只走一遍的方案,即為點陣的解
鄰接矩陣代碼如下:
/** * 鄰接矩陣 */ public class DenseGraph { // 節點數 private int n; // 邊數 private int m; // 是否為有向圖 private boolean directed; // 圖的具體數據 private boolean[][] g; //記錄節點的線數量 private int[] line; //已連接數量 private int connected; //換行專用 private int lineFeed; // 構造函數 public DenseGraph(){ n = 26; m = 0; directed = false; // g初始化為n*n的布爾矩陣, 每一個g[i][j]均為false, 表示沒有任和線 g = new boolean[n][n]; line=new int[n]; } //初始化點陣 private void initialization(){ //臨時變量,保存每個節點連接節點的數量,以判斷起點 int count=0; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (g[i][j]) { count++; } } line[i]=count; count=0; } } // 返回節點個數 public int V(){ return n;} // 返回邊的個數 public int E(){ return m;} // 向圖中添加一個邊 public void addEdge( int v , int w ){ assert v >= 0 && v < n ; assert w >= 0 && w < n ; if( hasEdge( v , w ) ) return; g[v][w] = true; if( !directed ) g[w][v] = true; m ++; } // 驗證圖中是否有從v到w的邊 boolean hasEdge( int v , int w ){ assert v >= 0 && v < n ; assert w >= 0 && w < n ; return g[v][w]; } //開始運行 public void start(){ //初始化 this.initialization(); //輸出鄰接矩陣 this.print(); //尋找起點 int qsd=-1; for (int i = 0; i < line.length; i++) { if (line[i]%2!=0){ qsd=i; break; }else if (line[i]!=0){ qsd=i; } } //從起點開始回溯尋找路線 flashBack(qsd); } public boolean flashBack(int a){ //如果已經走過的線數量等於總線數量,說明尋路完成 if (connected==m){ System.out.print("路線:["+a+"] -> "); return true; }else { //遍歷此點與全部節點的關係並按照以下執行 //1.如果兩點之間有連接,假設此路線為正確路線,將此線改變為無連接,並開始從此點遍歷 //2.如果最終無法走過全部線,則確定此路線不正確,回溯並將此線還原為連接,繼續遍歷 for (int i = 0; i < n; i++) { if (g[a][i]){ g[a][i]=false; g[i][a]=false; connected++; if (flashBack(i)){ lineFeed++; if (lineFeed%20==0) System.out.println(); System.out.print("["+a+"] -> "); return true; }else { connected--; g[a][i]=true; g[i][a]=true; } } } return false; } } //輸出鄰接矩陣 public void print(){ System.out.println("邊共:"+m+"條"); System.out.print("鄰接矩陣 "); for (int i = 1; i < n; i++) { System.out.print(i+" \t"); } System.out.println(); for (int i = 1; i < n; i++) { System.out.print(i+" \t"); for (int j = 1; j < n; j++) { System.out.print(g[i][j]+" \t"); } System.out.println(); } } }
使用方法:
創建對象(new)
添加邊(addEdge)
開始運行(start)
對於線非常多的圖,一條一條添加線依然很麻煩,有沒有更好的辦法呢?