• poj_1084 剪枝-IDA*


    题目大意

        给出一个由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*5
    using 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,剪枝1
    	if (complete_square_contain_match.empty()){
    		Destroy(n + 1, taken_num, left_complete_square);
    	}
    	else{
    		//如果火柴 match_index 是当前剩余的某个完整方块的构成火柴的最大的序号,则必须进行拆除,即剪枝不拆除的情况;剪枝2
    		if (!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){
    
    		//IDA2
    		next_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;
    		/*
    		IDA1
    		destroy_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或者2
    		Resolve(left_complete_square);
    		printf("%d
    ", gMinTakenNum);
    	}
    	return 0;
    }
    

     

  • 相关阅读:
    Codeforces Round #433 (Div. 2, based on Olympiad of Metropolises)
    树的遍历(前序中序求后序,后序中序求前序)
    Codeforces Round #439 (Div. 2)
    python3 调用百度api实现语音识别
    Win10 opencv cuda + 扩展库 vs2019 cuda10
    flask读取摄像头并实时显示
    树莓派设置开机启动
    树莓派python OLED使用
    UART Fingerprint Sensor (C)树莓派使用
    树莓派全版本换源(多环境测试无误版)
  • 原文地址:https://www.cnblogs.com/gtarcoder/p/4771229.html
Copyright © 2020-2023  润新知