二維平面深度優先-POJ 1020 Anniversary Cake

思路分析:

AC 代碼: 16 ms

// 思路:這個問題沒有巧妙的思路,於是只有貪心:能放下就放下,湊失敗了就換一個。 
// 將大蛋糕人爲劃分爲 SIZE * SIZE 個小格子,然後“從上到下,從左到右”依次填充大蛋糕。 

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
/*************** 全局變量 ******************/
char G[42][42];
int SIZE, n;			// 大蛋糕尺寸爲 SIZE * SIZE,小蛋糕有 n 個
int cases;				// 測試用例的數量
int a[18];				// 所有小蛋糕的邊長
bool visit_a[18];		// 所有小蛋糕被使用的情況
int col[42];			// 記載每一列被佔用的格子數
bool visit_map[18];		// 所有小蛋糕被使用的情況,是否被使用了
bool is_OK;			// 本問題是否可解

bool cmp(int a, int b){
	return a > b;
}
int min(int a, int b){
	return a > b ? b : a;
}

// used_cakes: 已經使用的小蛋糕數量
// col 是核心的數據,描述了當前大蛋糕被填充的情況
// 
void dfs(int used_cakes){
	// Step 1: Termination condition
	if (used_cakes == n || is_OK == true){
		is_OK = true;
		return;
	}
	// Step 2: 確定搜索範圍
	// 根據剪枝1,從前往後選
	for (int i = 0; i < n && is_OK==false; i++){
		// Step 3: 排除無效的候選對象
		if (visit_a[i] == true) continue;
		// 從前往後找,找某一列其佔用格子數最少
		// 坑:要找首次出現的佔用格子數最少的列,所以,搜索的順序應該是從後往前找,或者判斷條件爲 < 而不是 <= (細節)
		int min_ind = -1, min_val = 999999;
		for (int j = 0; j <SIZE; j++)
		if (col[j] < min_val) {
			min_ind = j;
			min_val = col[j];
		}
		if ( SIZE - col[min_ind] < a[i] || min_ind + a[i] -1 >= SIZE || col[min_ind+a[i]-1] != col[min_ind] ) 
			continue;   // 爲了加速程序,此處快速判斷


		int w_x = 0;		// 水平方向能容納的最大寬度
		int w_y = 0;		// 垂直方向能容納的最大寬度
		int w = 0;			// 綜合來看,當前能容納的最大寬度
		for (int j = min_ind; j < SIZE;j++)
		if (col[j] <= min_val)
			w_x++;
		else
			break;
		w_y = SIZE - col[min_ind];
		w = min(w_x, w_y);
		// 判斷是否能放得下
		if (a[i] > w) continue;

		// Step 4: 訪問當前結點(即,使用當前小蛋糕)
		visit_a[i] = true;
		int new_val = col[min_ind] + a[i];
		for (int j = min_ind; j <= min_ind + a[i] - 1; j++)
			col[j] += a[i]; // 之所以不是 col[j] += a[i],是考慮到了像俄羅斯方塊一樣,前一層有缺口
		// Step 5: 繼續下一步的搜索
		dfs(used_cakes + 1);
		if (is_OK == true) return;
		// Step 6: 放棄使用當前這個小蛋糕!
		for (int j = min_ind; j <= min_ind + a[i] - 1; j++)
			col[j] -= a[i];
		visit_a[i] = false;
	}
}

// used_cakes: 已經使用的小蛋糕數量
// col 是核心的數據,描述了當前大蛋糕被填充的情況
// 
void dfs_fast(int used_cakes){
	// Step 1: Termination condition
	if (used_cakes == n || is_OK == true){
		is_OK = true;
		return;
	}
	// Step 2: 確定搜索範圍
	// 剪枝:如果尺寸爲 x 的蛋糕,導致了失敗,則後面跳過所有尺寸爲 x 的蛋糕
	int failure_size = -1;
	for (int i = 0; i < n && is_OK == false; i++){
		// Step 3: 排除無效的候選對象
		if (visit_a[i] == true || a[i] == failure_size) continue;
		// 從前往後找,找某一列其佔用格子數最少
		// 坑:要找首次出現的佔用格子數最少的列,所以,搜索的順序應該是從後往前找,或者判斷條件爲 < 而不是 <= (細節)
		int min_ind = -1, min_val = 999999;
		for (int j = 0; j <SIZE; j++)
		if (col[j] < min_val) {
			min_ind = j;
			min_val = col[j];
		}
		if (SIZE - col[min_ind] < a[i] || min_ind + a[i] - 1 >= SIZE || col[min_ind + a[i] - 1] != col[min_ind])
			continue;   // 爲了加速程序,此處快速判斷


		int w_x = 0;		// 水平方向能容納的最大寬度
		int w_y = 0;		// 垂直方向能容納的最大寬度
		int w = 0;			// 綜合來看,當前能容納的最大寬度
		for (int j = min_ind; j < SIZE; j++)
		if (col[j] <= min_val)
			w_x++;
		else
			break;
		w_y = SIZE - col[min_ind];
		w = min(w_x, w_y);
		// 判斷是否能放得下
		if (a[i] > w) continue;

		// Step 4: 訪問當前結點(即,使用當前小蛋糕)
		visit_a[i] = true;
		int new_val = col[min_ind] + a[i];
		for (int j = min_ind; j <= min_ind + a[i] - 1; j++)
			col[j] += a[i]; // 之所以不是 col[j] += a[i],是考慮到了像俄羅斯方塊一樣,前一層有缺口
		// Step 5: 繼續下一步的搜索
		dfs_fast(used_cakes + 1);
		if (is_OK == true)
			return;
		else if (used_cakes == 0)
			return;
		else
			failure_size = a[i];
		// Step 6: 放棄使用當前這個小蛋糕!
		for (int j = min_ind; j <= min_ind + a[i] - 1; j++)
			col[j] -= a[i];
		visit_a[i] = false;
	}
}

int main(int argc, char * argv[])
{
	
	cin >> cases;
	while (cases--){
		/*************** Input ****************/
		// Initialization
		memset(G, 0, sizeof(G));
		memset(a, 0, sizeof(a));
		memset(visit_a, 0, sizeof(G));
		memset(col, 0, sizeof(col));

		is_OK = false;
		// Input
		int area = 0;
		int num_big_cakes = 0;
		scanf("%d%d", &SIZE, &n);
		for (int i = 0; i < n; i++)
		{
			scanf("%d", &a[i]);
			area += a[i] * a[i];
			if (a[i] > SIZE / 2) num_big_cakes++;
		}
		if (area != SIZE * SIZE || num_big_cakes > 1) {
			cout << "HUTUTU!" << endl;
			continue;
		}
		// Sort
		sort(a, a + n, cmp);

		/*************** Process ****************/
		dfs_fast(0);

		/*************** Output ****************/
		if (is_OK)
			cout << "KHOOOOB!" << endl;
		else
			cout << "HUTUTU!" << endl;
	}

	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章