1. 程式人生 > >ACM:搜尋演算法專題(1)——24點

ACM:搜尋演算法專題(1)——24點

題目描述:
給定4個數字,判定這4個數字是否可以通過運算得到結果24。運算操作包括:加、減、乘、除,允許變換數字的順序,允許使用括號改變運算順序。
    即:判定是否存在一種通過在下面的圓圈中新增運算子以及新增括號的方式使得等式成立:
           
a ○ b ○ c ○ d = 24
    例:數字 5, 5, 5, 1 可以通過運算得到結果24: 
 5 × (5 - 1 ÷ 5) = 24
        數字 9, 9, 9, 9 則無論通過怎樣的運算都無法得到24的結果。
解答:
    本題還是比較簡單的,由於資料量比較少,因此可以採用暴力搜尋的方式進行解答,列舉所有的可能的運算方式,如果有1種方案可以達到24,則表明給定的資料有解,否則無解。
    列舉的方式如下:
    由於加法和乘法滿足交換律,而減法和出發則不滿足,這裡我們新增兩種運算:“反減”和“反除”,分別記作 ~- 和 ~÷,此時:
                        a ~-  b = b -  a
                        a ~÷ b = b ÷ a
    新增這兩種運算操作後,就使得減法和除法也同樣符合交換律。此時,所有情況的運算順序就只有兩種情況,用※表示任意的運算子,則兩種運算順序為:
        ((a 
※ b) ※ c) ※ d))          <從左到右依次計算>
        ((a 
※ b) ※ (c ※ d))        <分別計算前兩個和後兩個數字的結果後,在將得到的兩個結果進行運算,得到最終的結果>

    而其他的運算順序均可以通過調整數字的排列順序得到用以上兩種情況表示的等價情況,例如:
        (a 
※ ((b ※ c) ※ d))    =====>  (((b ※ c) ※ d)※ a) ((a ※ (b ※ c)) ※ d)    =====>  (((b ※ c) ※ a) ※ d)
    這樣,通過列舉4個數字排列情況和三個位置的運算子的不同情況,就可以列舉到所有的運算情況。其中:4個數字排列,共有4!= 24種結果,而3個運算子中每個運算子都有6種不同的情況,因此共有6^3 = 216中情況,而運算順序又有2中情況,因此,總的情況數為:
                            24 × 216 × 2 = 10368
    由於加法和乘法本身就具有交換律,並且給定的4個數字也有某些數字相同的情況,因此,在實際列舉過程中,10368種情況會有某些是重複的。本題資料量比較小,因此重複計算的問題可以忽略。

輸入輸出格式:
    輸入:第1行:1個正整數, t,表示資料組數;第2..t+1行:4個正整數, a,b,c,d輸出:對每組測試資料輸出一行,表明能否計算出24點。若能夠輸出"Yes",否則輸出"No"。
資料範圍:
2 ≤ t ≤ 100
1 ≤ a,b,c,d ≤10

程式程式碼:

/****************************************************/
/* File        : Hiho_Week_98                       */
/* Author      : Zhang Yufei                        */
/* Date        : 2016-05-16                         */
/* Description : HihoCoder ACM program. (submit:g++)*/
/****************************************************/

#include<stdio.h>
#define DATA_TYPE float
#define INPUT_PATTERN "%f"

// Record the input data.
DATA_TYPE data[4];

/* Record the operator:
 *	0: Add		1: Substract	
 *  2: Multiply 3: Devide
 *  4: Reserve_Substract
 *	5: Reserve_Devide.
 */
int op_value;

// Record if the current calculate is legal.
int tag;

/*
 * This function computes the result according to given
 * data and operator.
 * Parameters:
 *		@a & @b: The data to compute.
 *		@op: The operator.
 * Returns:
 *		The result according to given data and operator,
 *		if the equation is illegal, returns -1.
 */
DATA_TYPE compute(DATA_TYPE a, DATA_TYPE b, int op) {
	switch(op) {
		case 0: return a + b; break;
		case 1: return a - b; break;
		case 2: return a * b; break;
		case 3: 
				if(b == 0) {
					tag = 0;
					return -1;
				} else {
					return a / b;
				}
				break;
		case 4: return b - a;
		case 5: if(a == 0) {
					tag = 0;
					return -1;
				} else {
					return b / a;
				}
				break;
	}
}

/*
 * This function checks if the data can get result 24.
 * Parameters:
 *		@index: The current index.
 * Returns:
 *		If the data can get result 24, returns 1,
 *		or returns 0.
 */
int check(int index) {
	if(index == 4) {
		for(op_value = 0; op_value < 216; op_value++) {
			int op1 = op_value % 6;
			int op2 = op_value / 6 % 6;
			int op3 = op_value / 36;

			tag = 1;
			if(compute(compute(
				compute(data[0], data[1], op1), data[2], op2),
				 data[3], op3) == 24 && tag) {
 					return 1;
			}
			
			tag = 1;
			if(compute(
				compute(data[0], data[1], op1), 
				compute(data[2], data[3], op3), op2) == 24 && tag) {
					return 1;
			}
		}
		
		return 0;
	}
	
	for(int i = index; i < 4; i++) {
		DATA_TYPE swap = data[index];
		data[index] = data[i];
		data[i] = swap;
		if(check(index + 1)) {
			return 1;
		}
		swap = data[index];
		data[index] = data[i];
		data[i] = swap;
	}
	
	return 0;
}

/*
 * This function deals with one test case.
 * Parameters:
 *		None.
 * Returns:
 *		None.
 */
void function(void) {
	for(int i = 0; i < 4; i++) {
		scanf(INPUT_PATTERN, &data[i]);
	}
	
	if(check(0)) {
		printf("Yes\n");
	} else {
		printf("No\n");
	}
}

/*
 * The main program.
 */
int main(void) {
	int t;
	scanf("%d", &t);
	
	for(int i = 0; i < t; i++) {
		function();
	}
	
	return 0;
}