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\n", gMinTakenNum);
}
return 0;
}
poj_1084 剪枝-IDA*的更多相关文章
- IDA*、剪枝、较难搜索、扫描——DNA sequence HDU - 1560
万恶之源 翻译 题意就是给出N个DNA序列,要求出一个包含这n个序列的最短序列是多长 这是一道搜索题,为什么呢?从样例可以感受到,我们应该从左往右"扫描",从n个DNA序列中取出某 ...
- HDU1560 DNA sequence IDA* + 强力剪枝 [kuangbin带你飞]专题二
题意:给定一些DNA序列,求一个最短序列能够包含所有序列. 思路:记录第i个序列已经被匹配的长度p[i],以及第i序列的原始长度len[i].则有两个剪枝: 剪枝1:直接取最长待匹配长度.1900ms ...
- BZOJ1085 [SCOI2005]骑士精神(IDA*)
IDA*是IDS的基础上加上满足A*算法的估值函数来剪枝的搜索算法. 这题代码量挺少的,可以看出整个IDA*的框架: #include<cstdio> #include<cstrin ...
- [poj2286]The Rotation Game (IDA*)
//第一次在新博客里发文章好紧张怎么办 //MD巨神早已在一个小时前做完了 The Rotation Game Time Limit: 15000MS Memory Limit: 150000K To ...
- IDA*
模拟退火 基本思路(Main Thoughts): IDA*是一种优秀的搜索法,在一般的实际问题中,它比普通的搜索更快. 通过迭代加深和估价函数剪枝来搜索. 通常处理没有层数上界或上界很多大的搜索. ...
- 【Uva11212】 Editing a Book(IDA*)
[题意] 有n个数字的全排列,每次可以剪切一段粘贴到某个位置.问最后变成升序最少多少步. 如"{2,4,1,5,3,6}要2步 {3,4,5,1,2}只要一步 [分析] 迭代深搜真的AC了也 ...
- [BZOJ 1085] [SCOI2005] 骑士精神 [ IDA* 搜索 ]
题目链接 : BZOJ 1085 题目分析 : 本题中可能的状态会有 (2^24) * 25 种状态,需要使用优秀的搜索方式和一些优化技巧. 我使用的是 IDA* 搜索,从小到大枚举步数,每次 DFS ...
- POJ2286 The Rotation Game(IDA*)
The Rotation Game Time Limit: 15000MS Memory Limit: 150000K Total Submissions: 5691 Accepted: 19 ...
- HDU 1813 Escape from Tetris (IDA*)
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=1813 题意:给你一个n*n的迷宫,其中0代表有一个人在这个位置,1代表墙,现在要求一个路线,使所有的人通 ...
随机推荐
- Hive中order by,sort by,distribute by,cluster by的区别
一:order by order by会对输入做全局排序,因此只有一个Reducer(多个Reducer无法保证全局有序),然而只有一个Reducer,会导致当输入规模较大时,消耗较长的计算时间.关于 ...
- 使用RAID与LVM磁盘阵列技术。
7.2 LVM逻辑卷管理器 前面学习的硬盘设备管理技术虽然能够有效地提高硬盘设备的读写速度以及数据的安全性,但是在硬盘分好区或者部署为RAID磁盘阵列之后,再想修改硬盘分区大小就不容易了.换句话说,当 ...
- vmware克隆Centos6.4虚拟机网卡无法启动问题
vmware克隆Centos6.4虚拟机网卡无法启动问题 2014-02-26 16:44:54 标签:老男孩培训 vmware克隆问题 网卡无法启动 ...
- kettle的报错解决机制
在kettle执行的过程中,如果遇到错误,kettle会停止运行.在某些时候,并不希望kettle停止运行,这时候可以使用错误处理(Step Error Handling).错误处理允许你配置一个步骤 ...
- SparkR:数据科学家的新利器
摘要:R是数据科学家中最流行的编程语言和环境之一,在Spark中加入对R的支持是社区中较受关注的话题.作为增强Spark对数据科学家群体吸引力的最新举措,最近发布的Spark 1.4版本在现有的Sca ...
- [Spring] Java spring quartz 定时任务
首先,需要导入quartz 的jar包 ① applicationContext.xml <!-- 轮询任务 --> <import resource="classpath ...
- C语言 float、double数据在内存中的存储方式
float在内存中占4个字节(32bit),32bit=符号位(1bit)+指数位(8bit)+底数位(23bit) 指数部分 指数位占8bit,可以表示数值的范围是0-(表示0~255一共256个数 ...
- 关于jsp,javascript,php等语言
技术一 jsp: java植入html 技术二 javascript(js)植入html 技术三早期php植入html 弱类型语言和强类型语言 弱类型语言无法实现函数重载,没办法
- 【转】【Mysql】MySQL添加用户、删除用户与授权
MySql中添加用户,新建数据库,用户授权,删除用户,修改密码(注意每行后边都跟个;表示一个命令语句结束): 1.新建用户 1.1 登录MYSQL: @>mysql -u root -p @&g ...
- 无法从“重载函数类型”为“const std::_Tree<_Traits> &”推导 <未知> 参数
场景: 原因: 用到string类型,但是没有包含头文件. 解决方法: #include<string>