標籤:
題目大意
給出一個由2*S*(S+1)構成的S*S大小的火柴格。火柴可以構成1x1,2x2...SxS大小的方格。其中已經拿走了幾個火柴,問最少再拿走幾個火柴可以使得這些火柴無法構成任何一個方格。
題目分析
考慮每個火柴被幾個方格佔用,這樣似乎可以使用貪心演算法來解,每次都選擇當前剩餘的火柴中被完整的方格佔用次數最多的那根火柴。理論上感覺可以,具體沒去實現。。。
本題,採用的是搜尋+剪枝來實現。需要做的是儲存每個搜尋節點的狀態,以及通過合理的記錄資料,對狀態進行推演。
這裡狀態為:當前需要被拆除的火柴序號(match_index,可以拆除或者不拆除)+當前剩餘的完整的方格的數目(left_square_num)+
當前已經拆除的火柴數目(taken_num,可以用於最佳化剪枝)。
而記錄資料可以為:火柴i是否位於方塊j中 gMatchInSquare[i][j]. 方塊s中最大的火柴序號 gMaxMatchInSquare[s](用於剪枝)。
這樣,使用最佳化剪枝,DFS搜尋。剪枝:
(1)對於當前節點,若taken_num > gMinTakenNum,則剪枝返回;
(2)如果火柴 match_index 不存在任何一個剩餘的完整的方塊中,則不必拆除match_index,即剪枝拆除match_index的情況;
(3)如果火柴 match_index 是當前剩餘的某個完整方塊的構成火柴的最大的序號,則必須進行拆除(因為,對於火柴是按照序號從小到大進行遞迴搜尋,如果match_index為某個方格的最大序號,則若不刪除,之後的任何火柴都不在該方格中,無法破壞該方格),即剪枝不拆除的情況;
單純使用以上剪枝,仍然會逾時,則考慮使用估計函數來進行深度剪枝:考慮當前剩餘的所有完整方格中不相交的方格的個數K,則從目前狀態開始,至少還需要拆除K個火柴,才可能達到沒有完整方格的狀態。因此 taken_num >= gMinTakenNum改為
taken_num + SeperateCompleteSquareNum() > gMinTakenNum,進行剪枝。
實現方法
可以採用單純的剪枝,或者採用IDA演算法。
實現(c++)
#define _CRT_SECURE_NO_WARNINGS#include<stdio.h>#include<vector>#include<algorithm>#define INFINITE 1 << 30#define MAX_MATCH_NUM 2*5*6#define MAX_SQUARE_NUM MAX_MATCH_NUM*5using namespace std;bool gMatchInSquare[MAX_MATCH_NUM][MAX_SQUARE_NUM];//判斷火柴i是否位於方塊j中bool gSquareComplete[MAX_SQUARE_NUM];//方塊s是否完整int gMaxMatchInSquare[MAX_SQUARE_NUM];//方塊s中最大的火柴序號int gMinTakenNum;//最少需要拿走的火柴數目int gTotalSquareNum;//沒有任何火柴被拿走的情況下,總的方格數目int gTotalMatchNum;//沒有任何火柴被拿走的情況下,總的火柴數vector<int> gNotMissedMatch;//沒有被拿走的火柴集合,從中選擇拿走的火柴//初始化,主要是對於S*S的網格,判斷 每個火柴位於那些方格中,以及每個方格中的最大的火柴序號void Init(int size){memset(gMatchInSquare, false, sizeof(gMatchInSquare));memset(gSquareComplete, true, sizeof(gSquareComplete));gTotalMatchNum = 2 * (size + 1)*size;int s = size;gTotalSquareNum = 0;while (s > 0){gTotalSquareNum += s*s;s--;}s = 1;int total_square_index = 0;while (s <= size){for (int square_index = 0; square_index < (size - s + 1)*(size - s + 1); square_index++){int match_index = (square_index / (size - s + 1))*(2 * size + 1) + (square_index % (size - s + 1));int up_beg = match_index;int left_beg = match_index + size;int right_beg = left_beg + s;int down_beg = up_beg + s*(1 + size*2);for (int i = 0; i < s; i++){gMatchInSquare[up_beg + i][total_square_index] = true;gMatchInSquare[down_beg + i][total_square_index] = true;gMatchInSquare[left_beg + i*(2 * size + 1)][total_square_index] = true;gMatchInSquare[right_beg + i*(2 * size + 1)][total_square_index] = true;}gMaxMatchInSquare[total_square_index] = down_beg + s - 1;total_square_index++;}s++;}}//判斷火柴m位於那些完整的方格中,以及m是否是某些網格的最大序號火柴void MatchInCompleteSquare(int m, vector<int>& complete_square_contain_match, bool* match_is_max){*match_is_max = false;for (int s = 0; s < gTotalSquareNum; s++){if (gMatchInSquare[m][s] && gSquareComplete[s]){complete_square_contain_match.push_back(s);if (gMaxMatchInSquare[s] == m){*match_is_max = true;}}}}//獲得當前剩餘的完整網格中,不相交的網格的數目int SeperateCompleteSquareNum(int n){int result = 0;typedef pair<int, int> MatchNumSquarePair;vector<MatchNumSquarePair> ms_vec;for (int s = 0; s < gTotalSquareNum; s++){if (!gSquareComplete[s])continue;int num = 0;for (int m = 0; m < gTotalMatchNum; m++){if (gMatchInSquare[m][s])num++;}ms_vec.push_back(MatchNumSquarePair(num, s));}sort(ms_vec.begin(), ms_vec.end());vector<bool> match_used(gTotalMatchNum, false);for (int i = 0; i < ms_vec.size(); i++){MatchNumSquarePair ms_pair = ms_vec[i];bool ok = true;for (int m = n; m < gTotalMatchNum; m++){if (match_used[m] && gMatchInSquare[m][ms_pair.second]){ok = false;}}if (ok){for (int m = n; m < gTotalMatchNum; m++){if (gMatchInSquare[m][ms_pair.second]){match_used[m] = true;}}result++;}}return result;}/*//單純的估計函數進行剪枝,不適用IDA演算法void Destroy(int n, int taken_num, int left_complete_square){if (n == gNotMissedMatch.size()){return;} if (left_complete_square == 0){gMinTakenNum = gMinTakenNum < taken_num ? gMinTakenNum : taken_num;return;}//估價函數剪枝if (taken_num + SeperateCompleteSquareNum(gNotMissedMatch[n]) >= gMinTakenNum){return;}int match = gNotMissedMatch[n];vector<int> complete_square_contain_match;bool match_is_max_in_square;MatchInCompleteSquare(match, complete_square_contain_match, &match_is_max_in_square);//如果火柴 match_index 不存在任何一個剩餘的完整的方塊中,則不必拆除match_index,剪枝1if (complete_square_contain_match.empty()){Destroy(n + 1, taken_num, left_complete_square);}else{//如果火柴 match_index 是當前剩餘的某個完整方塊的構成火柴的最大的序號,則必須進行拆除,即剪枝不拆除的情況;剪枝2if (!match_is_max_in_square){Destroy(n + 1, taken_num, left_complete_square);}for (int i = 0; i < complete_square_contain_match.size(); i++){int s = complete_square_contain_match[i];gSquareComplete[s] = false;}Destroy(n + 1, taken_num + 1, left_complete_square - complete_square_contain_match.size());for (int i = 0; i < complete_square_contain_match.size(); i++){int s = complete_square_contain_match[i];gSquareComplete[s] = true;}}}*//*//IDA 迭代加深,每次只增加1個深度void Destroy(int n, int taken_num, int left_complete_square, bool* destroy_over){if (*destroy_over)return;if (n == gNotMissedMatch.size()){return;}if (left_complete_square == 0){*destroy_over = true;return;}int seperate_complete_square_num = SeperateCompleteSquareNum(gNotMissedMatch[n]);if (taken_num + seperate_complete_square_num > gMinTakenNum){return;}int match = gNotMissedMatch[n];vector<int> complete_square_contain_match;bool match_is_max_in_square;MatchInCompleteSquare(match, complete_square_contain_match, &match_is_max_in_square);if (complete_square_contain_match.empty()){Destroy(n + 1, taken_num, left_complete_square, destroy_over);}else{if (!match_is_max_in_square){Destroy(n + 1, taken_num, left_complete_square, destroy_over);}for (int i = 0; i < complete_square_contain_match.size(); i++){int s = complete_square_contain_match[i];gSquareComplete[s] = false;}Destroy(n + 1, taken_num + 1, left_complete_square - complete_square_contain_match.size(), destroy_over);for (int i = 0; i < complete_square_contain_match.size(); i++){int s = complete_square_contain_match[i];gSquareComplete[s] = true;}}}*///IDA迭代加深,每次可能增加多個深度,由next_min_taken_num指定void Destroy(int n, int taken_num, int left_complete_square, int & next_min_taken_num){if (next_min_taken_num <= gMinTakenNum){return;}if (n == gNotMissedMatch.size()){return;}if (left_complete_square == 0){next_min_taken_num = next_min_taken_num < taken_num ? next_min_taken_num : taken_num;return;}int seperate_complete_square_num = SeperateCompleteSquareNum(gNotMissedMatch[n]);if (taken_num + seperate_complete_square_num > gMinTakenNum){next_min_taken_num = next_min_taken_num < taken_num + seperate_complete_square_num ? next_min_taken_num : seperate_complete_square_num + taken_num;return;}int match = gNotMissedMatch[n];vector<int> complete_square_contain_match;bool match_is_max_in_square;MatchInCompleteSquare(match, complete_square_contain_match, &match_is_max_in_square);if (complete_square_contain_match.empty()){Destroy(n + 1, taken_num, left_complete_square, next_min_taken_num);}else{if (!match_is_max_in_square){Destroy(n + 1, taken_num, left_complete_square, next_min_taken_num);}for (int i = 0; i < complete_square_contain_match.size(); i++){int s = complete_square_contain_match[i];gSquareComplete[s] = false;}Destroy(n + 1, taken_num + 1, left_complete_square - complete_square_contain_match.size(), next_min_taken_num);for (int i = 0; i < complete_square_contain_match.size(); i++){int s = complete_square_contain_match[i];gSquareComplete[s] = true;}}}//IDA方法void Resolve(int left_complete_square){gMinTakenNum = SeperateCompleteSquareNum(gNotMissedMatch[0]);int next_min_taken_num;bool destroy_over;while (true){//IDA2next_min_taken_num = INFINITE;Destroy(0, 0, left_complete_square, next_min_taken_num);if (next_min_taken_num <= gMinTakenNum){gMinTakenNum = next_min_taken_num;return;}gMinTakenNum = next_min_taken_num;/*IDA1destroy_over = false;Destroy(0, 0, left_complete_square, &destroy_over);if (destroy_over){return;}gMinTakenNum++;*/}}int main(){int T;scanf("%d", &T);while (T--){int size, k;scanf("%d %d", &size, &k);Init(size);gNotMissedMatch.clear();for (int i = 0; i < gTotalMatchNum; i++){gNotMissedMatch.push_back(i);}gMinTakenNum = INFINITE;int missed_match_index, left_complete_square = gTotalSquareNum;for (int i = 0; i < k; i++){scanf("%d", &missed_match_index);missed_match_index--;gNotMissedMatch.erase(find(gNotMissedMatch.begin(), gNotMissedMatch.end(), missed_match_index));for (int j = 0; j < gTotalSquareNum; j++){if (gMatchInSquare[missed_match_index][j] && gSquareComplete[j]){gSquareComplete[j] = false;left_complete_square--;}}}//普通的 估價剪枝//Destroy(0, 0, left_complete_square);//IDA 1或者2Resolve(left_complete_square);printf("%d\n", gMinTakenNum);}return 0;}
poj_1084 剪枝-IDA*