POJ 1084 搜索(IDA*)

来源:互联网 发布:番茄鸡蛋打卤面 知乎 编辑:程序博客网 时间:2024/05/17 23:08

题意:给一个由(n+1)*n*2根火柴棒构成的边长为n正方形,每根火柴棒都有编号,已经删去了k根火柴棒,问至少再删多少根火柴棒可以破坏掉所有正方形。 【刘汝佳:《算法竞赛入门经典(第二版)》 P213】

【小吐槽】
感觉做这道题的历程真是哔了狗了。一开始自己写了个暴搜果断就T了,主要是判断每个正方形存在与否的方法太暴力。之后在网上看到很多人都写的DLX,被如何记录正方形存在的方式恶心到的我下狠心决定学一学DLX。一开始看刘汝佳叔叔的书,懂了大概原理,不过他的书上的代码是精确覆盖的,而这道题是可重复覆盖。所以自己得yy代码,yy+copy网上其他。然而被DLX搞了2天时间,还是有问题,写出来的样例都过不了,哎,还是自己太弱的过。暂时还不适合学这种高大上的东西T^T。无奈了,调DLX代码调的头疼了,决定再写一遍IDA*,沿用了一些表示正方形的特殊技巧,刚刚写完之后抱着肯定会Wa的心态随便交了一下,0ms AC。

那么就是说,这道题我是用的是IDA*,并没有用高大上的舞蹈链。
估价函数还是很好想的,麻烦就麻烦在如何判断在一个状态下还剩哪些正方形。感觉对于乱搞能力强的这个问题都不是事。

我们开两个数组stick[][], square[][] (我用的是vector), stick[i]表示的是需要编号为i的木棒的正方形有哪些,square[i]表示的是编号为i的正方形需要的木棒有哪些。

exi[i]表示的是编号i的正方形是否存在:exi[i] == 0, 存在;每删去该正方形上的一根火柴,exi[i]–。这么做就解决了递归、回溯的问题。

观察一下木棒的编号规律,提前预处理出来正方形们以及木棒们(square,stick两个数组),开始搜索,找到一个还存在的正方形,枚举删除它的哪一根木棒,递归。为了节省时间,要找还存在的边长最小的正方形,这个在我们预处理square数组时,加入正方形的顺序就可以搞定。

估价函数:对于当前一个状态,枚举每个正方形是否存在,如果存在,就删除它的所有木棍,同时res++,然后继续枚举,最后返回res。 这个估价函数和poj 2032 IDA*做法用的那个估价函数很相似。

#include <cstdio>#include <algorithm>#include <cstring>#include <vector>using namespace std;int T, n, k, totstick, totsquare, neibor, ans, maxd, exi[65], temp[65];vector <int> stick[65];     // squares that need stick[i]vector <int> square[65];    // sticks that square[i] needint h(){    int res = 0;    for(int i = 1; i <= totsquare; i++) temp[i] = exi[i];    for(int i = 1; i <= totsquare; i++) if(!temp[i])    {        res ++;        temp[i] -= square[i].size();        for(int j = 0; j < square[i].size(); j++)         for(int l = 0; l < stick[square[i][j]].size(); l++)        {            temp[stick[square[i][j]][l]] --;        }    }    return res;}bool dfs(int sum){    if(sum+h() >= maxd) return 0;    int tmp = 1;    while(exi[tmp] < 0 && tmp <= totsquare) tmp++;  // choose a square (tmp)    if(tmp > totsquare) {ans = min(sum, ans); return 1;}    for(int i = 0; i < square[tmp].size(); i++)  // choose which in tmp to destroy    {        int sti = square[tmp][i];        for(int j = 0; j < stick[sti].size(); j++) exi[stick[sti][j]]--;        if(dfs(sum+1)) return 1;        for(int j = 0; j < stick[sti].size(); j++) exi[stick[sti][j]]++;    }    return 0;}int main(){    scanf("%d", &T);    while(T--)    {        scanf("%d %d", &n, &k);        totstick = (n+1)*n*2; totsquare = 0; neibor = n*2+1;         for(int i = 1; i < 65; i++)        {            stick[i].clear(); square[i].clear();        }           for(int sz = 1; sz <= n; sz++)        for(int i = 1; (i-1)/neibor+sz <= n; i += neibor)        for(int j = i; j-i+sz <= n; j++)        {            totsquare ++;            for(int l = j; l-j < sz; l++)            {                square[totsquare].push_back(l);                square[totsquare].push_back(l+sz*neibor);                stick[l].push_back(totsquare);                stick[l+sz*neibor].push_back(totsquare);               }            for(int l = j+n; (l-j-sz)/neibor < sz; l += neibor)            {                square[totsquare].push_back(l);                square[totsquare].push_back(l+sz);                stick[l].push_back(totsquare);                stick[l+sz].push_back(totsquare);                   }        }        memset(exi, 0, sizeof exi);        for(int i = 1; i <= k; i++)        {            int t; scanf("%d", &t);            for(int j = 0; j < stick[t].size(); j++)            {                exi[stick[t][j]]--;            }            totstick --;        }        ans = totstick;        for(maxd = 1; ; maxd++) if(dfs(0)) break;        printf("%d\n", ans);    }    return 0;}
0 0