1. 程式人生 > >最簡單24點演算法,可任意實現n數n點,一看就明!

最簡單24點演算法,可任意實現n數n點,一看就明!

介紹

網上的24點演算法基本上都專注於4張牌湊24點,有的演算法甚至枚舉了所有括號的組合,讓人看得頭暈眼花。這些演算法若是推廣到n個數湊n點,基本都歇菜了。

本演算法採用暴力列舉法,能處理任意個數湊任意值以24點為例,本演算法會枚舉出4個數+3個運算子能組成的所有組合,每種組合當作字尾表示式構造成為表示式樹,得出結果。

列舉所有可能的字尾表示式

列舉所有排列的演算法就是套用遞迴模板。

以24點為例,4個數,必然需要3個運算子。我們的目的就是找到這4數3符的所有排列方式。

第一步:枚舉出“從加減乘除四種運算子中拿出三個運算子”的所有情況。

這可不是A43=4這樣簡單,因為每種運算子可以重複。(比如“+++”、“---”這樣)。最簡單的辦法是無腦枚舉出所有情況(4*4*4=64種)然後刪除重複的。或者如下面genOpPermute(int n)方法所做的,列舉的過程中剪掉重複的。最終會留下20種組合。

第二步:將枚舉出的20種運算子組合與4個數字整合成一個String陣列。分別找出20個String陣列的全排列

比如四個數是8,7,4,7,那麼我就有了 {8,7,4,7,+,+,+} {8,7,4,7,-,-,-}...等20種組合。對每種組合,我們都要找到其全排列。全排列的演算法下面有,注意要避免重複的情況,如8 7 4 7 和 8 7 4 7應算是同一種情況。

package helpers;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class PermuteGen {
	
	private static char[] ops=new char[] {'+','-','*','/'};
	
	
	
	/**
	 * 加減乘除選n個 可重複選 所有選法
	 * @param n
	 * @return
	 */
	public static List<List<Character>> genOpPermute(int n){
		ArrayList<Character> tempList=new ArrayList<Character>();
		List<List<Character>> ans=new ArrayList<List<Character>>();
		opBacktrack(n,tempList,ans,new HashSet<Integer>());//用HashSet儲存運算子的乘積,已達到去除重複的目的(+*/和*/+算是一種情況,這兩種情況三個char的乘積是相同的)		return ans;
	}
	
	private static void opBacktrack(int n,ArrayList<Character> tempList,List<List<Character>> ans,HashSet<Integer> set) {
		if(tempList.size()==n) {
			int i=1;
			for(char c:tempList) {
				i*=c;
			}
			if(!set.contains(i)) {
				ans.add(new ArrayList<Character>(tempList));
				set.add(i);
			}
		}else {
			for(int i=0;i<4;i++) {
				tempList.add(ops[i]);
				opBacktrack(n,tempList,ans,set);
				tempList.remove(tempList.size()-1);
			}
		}
	}
	
	/**
	 * 輸出n個String的全排列
	 * @param n
	 * @return
	 */
	public static List<List<String>> genStrPermute(String[] strs){
		ArrayList<String> tempList=new ArrayList<String>();
		List<List<String>> ans=new ArrayList<List<String>>();
                Arrays.sort(strs);//先排序,讓相同元素相鄰
		strBacktrack(strs,tempList,ans,new boolean[strs.length]);//由於可能含有重複元素(一次抽出兩張2等) 必須有一個數組儲存各個元素的訪問狀態
		return ans;
	}
	
	private static void strBacktrack(String[] strs,ArrayList<String> tempList,List<List<String>> ans,boolean[] used) {
		if(tempList.size()==strs.length) {
			ans.add(new ArrayList<String>(tempList));
		}else {
			for(int i=0;i<strs.length;i++) {
				if(!(used[i] || i>0 && strs[i].equals(strs[i-1]) && !used[i-1])) {
//這裡的邏輯是:當這個元素已被訪問過,則這次不訪問 
//或者 
//兩個連續的相同元素 若前一個還沒被訪問過 後面的堅決不訪問!
//這就相當於給這些重複元素規定了內在的順序,避免了重複!
					tempList.add(strs[i]);
					used[i]=true;
					strBacktrack(strs,tempList,ans,used);
					tempList.remove(tempList.size()-1);
					used[i]=false;;
				}
			}
		}
	}
	
	
}

構建表示式樹

表示式樹沒什麼特別的,直接用以前作業改。唯一需要注意的點就是,用枚舉出的所有組合構建表示式樹的過程中一定會出現無法成功建樹的情況。比如+++1234一定無法構建表示式樹,我們需要對這種情況進行甄別。簡單的方法就是看最終構建表示式樹的棧是不是隻剩下1個結點。如果能在生成全排列的過程中剪枝掉不合格的字尾表示式,本演算法甚至不需要用表示式樹,不過本演算法突出一個簡單,最大化利用已有資源,就不考慮那些事情了。

public boolean build (List<String> expr )  // Build tree from prefix expression
    {             
    		
    		Stack<ExprTreeNode> nodeStack=new Stack<ExprTreeNode>();
    		
    		for(int i=0;i<expr.size();i++) {
    			String c=expr.get(i);

    				
				if(isDigit(c)) {
					nodeStack.push(new ExprTreeNode(c,null,null));
    			}

    			else {
    				ExprTreeNode left=null;
    				ExprTreeNode right=null;
    				try {
    					left=nodeStack.pop();
    					right=nodeStack.pop();
    				}catch(Exception e) {
    					
    				}
    				
    				ExprTreeNode opNode=new ExprTreeNode(c,left,right);
    				nodeStack.push(opNode);
    			}
    		}
    		if(nodeStack.size()==1) {//僅當棧內只有1個節點的時候,表示式樹才算構造成功
    			this.root=nodeStack.pop();
    			return true;
    		}
    		return false;
    	
    	
    }

當然,表示式樹裡還封裝了計算出字尾表示式結果的方法。算一下看看是否等於24就可以了!

圖形介面

筆者亦製作了配套的圖形介面。

不足之處

由於本演算法是枚舉出所有情況,複雜度相對較高,並且對交換律未作優化,會產生冗餘答案,忘讀者批評指正!

相關推薦

簡單24演算法任意實現nn

介紹 網上的24點演算法基本上都專注於4張牌湊24點,有的演算法甚至枚舉了所有括號的組合,讓人看得頭暈眼花。這些演算法若是推廣到n個數湊n點,基本都歇菜了。 本演算法採用暴力列舉法,能處理任意個數湊任意值。以24點為例,本演算法會枚舉出4個數+3個運算子能組成的所

關於單登入原理與簡單實現寫的太好了

一、單系統登入機制1、http無狀態協議web應用採用browser/server架構,http

Word文件自動生成目錄方法還會自動更新

無論是寫論文還是工作中,有時候需要設定Word文件的目錄,但是那麼多頁的文件,一個一個手動去新增太麻煩了,究竟有什麼好辦法可以讓Word文件自動生成目錄呢?相信大家都想知道,那今天就讓小編給大家講講Word文件自動生成目錄的方法吧,絕對簡單,保證大家看完就會! 1、先設定好文章的標題樣式 首先,需要把文章

Word文檔自動生成目錄方法還會自動更新

引用 有時 文本 images 圖片 鼠標 添加 麻煩 所有 無論是寫論文還是工作中,有時候需要設置Word文檔的目錄,但是那麽多頁的文檔,一個一個手動去添加太麻煩了,究竟有什麽好辦法可以讓Word文檔自動生成目錄呢?相信大家都想知道,那今天就讓小編給大家講講Word文檔自

關於vue下跨域問題明白

最終還是遇到了跨域問題,經過一下午的各種嘗試終於成功的掉到了想要的東西,下面就來寫一下是如何實現的,也算是給後來者填個坑: 你需要做一個反向代理的東西開啟你的vue專案的config資料夾下的index.js 然後找到以下的程式碼: dev:

安卓混合開發——原生Java和H5互動保證你

** 在Android開發中,越來越多的商業專案使用了Android原生控制元件與WebView進行混合開發,當然不僅僅就是顯示一個WebView那麼簡單,有時候還需要本地Java程式碼與HTML中的JavaScript進行互動,Android也對互動做了很好的封裝,所以很容易實現例如:點選網頁中的按鈕An

相見恨晚的 Git 命令動畫演示

![](https://user-gold-cdn.xitu.io/2020/4/7/17152edb7d10776f?w=879&h=372&f=png&s=106060) 雖然 Git 是一個強大的工具,但是我覺得大部分人都會同意我說的:它也可以是一個……噩夢!我一直覺得,使用 Git 的時候把操作過

保姆級別的RabbitMQ教程(有安裝教程送安裝需要的依賴包送Java、Golang兩種客戶端教學Case)

[TOC] ### 什麼是AMQP 和 JMS? **AMQP**:即Advanced Message Queuing Protocol,是一個應用層標準高階訊息佇列協議,提供統一訊息服務。是應用層協議的一個開放標準,為面向訊息的中介軟體設計。基於此協議的客戶端與訊息中介軟體可傳遞訊息,並不受客戶端/中介

簡單的LRU演算法實現執行緒安全的

LRU演算法用途之廣就不說了,凡是要用cache的地方都可以見到它的身影。特別是執行緒多,併發高,資料量大的環境下。jdk1.5真好,在LinkedHashMap.java的原始碼中直接有這樣的字樣、“This kind of map is well-suited to bu

安卓開發-簡單快速的仿微信聊天實現-附贈微信原生表情QQ原生表情

前言;正常實現聊天功能想必大家都使用三方的Sdk比如環信融雲集成的,但是聊天記錄的儲存只能有三天,想增加儲存時長就需要花錢,so 我只好自己想辦法實現了,這個demo是類似於留言板,並非即時通訊!只實現了表情文字圖文混排,可以通過手動重新整理實現即時通訊ok廢話少說,先看

hdu 2037(簡單的貪心演算法)

#include<iostream> using namespace std; struct tv { int s; int e; }a[105]; int cmp(

簡單的android studio2.3安裝 ButterKnife 8.5.1版本方法解放雙手

Android ButterKnife Zelezny是個很好用的工具,經常會用到,開發的時候經常會做一些搬運工的事,這個工具就是可以一鍵幫我們搬了!最近android studio 升級到了2.3,我就升級到了Android Studio 2.3正式版,畢竟作為一名IT

簡單的排序演算法之一氣泡排序----js實現

1. 演算法步驟 比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。 對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對。這步做完後,最後的元素會是最大的數。 針對所有的元素重複以上

簡單的KMP演算法求next陣列值的方法

本文依照嚴蔚敏串的資料結構(C語言版本)總結的方法: next陣列的求解方法是: 注意:1.   j的下標識從0開始排的                 2.  規定next[0]=-1,next[1]=0                   j           

android studio簡單的更新方式(像普通軟體一樣直接線上升級誰說as不能線上升級的???)

I. Open your Android Studio's bin directory, such as 'D:\Android Studio\bin'  II. Using text editor to edit Android Studio config file. For x64 system, ope

vue select的change事件擊過的城市名存在組中下次調用不需要再調用接口

toast input url status fin -a false padding left <template> <div id="body" class="application" v-show="show" v-cloak> &

簡單的神經網絡-感知器-python實現

nbsp ges mat lob date def global pytho .com import numpy as np import matplotlib.pyplot as plt X=np.array([[1,3,3], [1,4,3],

activiti6.0 提交流程至某節點 用於實現駁回操作(未測試)

完成 activit sets exce condition d3d pri class mit /** * @param task 任務Id * @param variables ... * @param targetActivity

史上簡單的springboot國際化多語言切換實現方案

messages conf main del span 語言 rop target 每天 每天學習一點點 編程PDF電子書、視頻教程免費下載:http://www.shitanlife.com/code 前提: 在resources目錄下建立 messages_en

晚上9的北京地鐵站碼農角落敲程式碼網友:知線上bug

作為一名程式設計師,想必大多數人都有著解決線上問題的經歷吧,既然是線上問題,肯定是刻不容緩,每時每刻都在影響著使用者的體驗與公司的營收情況,為了讓損失能夠最小化,偉大的程式設計師們就必須做出一點犧牲去解決這件事情了,當然,大部分程式設計師都是比較敬業的,對於隨時隨地解決線上問題的意識都是有的,經常能