POJ 2032 搜索 (IDA*) 或 DLX

来源:互联网 发布:linux连接数据库命令 编辑:程序博客网 时间:2024/04/30 09:35

题意:

你有一间矩形的屋子,屋子的地面上铺着正方形的地板,共有 H 行 W 列。经过长时间的使用,一些地板损坏了。而你又没有钱将整间屋子的地板都换成新的,所以你决定用一些地毯将损坏的地毯盖起来。
然而,你需要遵守下面的注意事项:
所有的地毯都必须是正方形的。
地毯可以相互覆盖。
地毯可以是任意大小的。
地毯不能盖到屋子外面。
所有的损坏的地板都要被盖住。
所有的未损坏的地板都不能被盖住。

这道题本来是暴搜加很多剪枝,自己原来的T了,看了网上题解有IDA*就被吓到了,因为之前没有写过IDA*,而且对于IDA*还处于一个不了解的状态。不过看到也有向我一样写暴搜加剪枝的题解,其中有几个我没用到的剪枝,之后加在我的代码中交上去却W了,拍一拍也没发现什么问题,但是不想调了,决定学一下IDA*。

/*
先看了看刘汝佳叔叔的紫书,在IDDFS的介绍中加了一行,就是提到了IDA*,说这个是表达一下IDA*并没有A*那么悚[自己蒟蒻]听闻,IDA*可以大概理解为IDDFS+A*的h函数。IDDFS是在DFS的基础上,限制搜索的深度,当搜索深度超过预定值Maxd时就return,在搜到解之前不断加大Maxd,直到搜索到解。Maxd初值很小(一般从0开始循环)。
然而在这个迭代加深搜索的过程中加一个估价函数h,就可以说是IDA*了,h函数的作用是估算当前状态到达解需要多少步,而如果当前已走步数(或深度)加上这个估算值大于已知ans的话就可以return了。
*/

以上只是很浅显的一个理解,感觉这道题并不能算是IDA*的一个很好的例题,它单单是在搜索的过程中加了h函数,并没有体现”ID”,自己在理解上也有一些问题:IDDFS搜索任一可行解以及DFS+h函数搜索最优解可以很好理解,但IDA*怎么搜这两种解感觉还有些问题,等下再写一道经典一点的例题加深一下理解吧。

那么来看这道题的做法:
我是先给所有点都编了号,id(x, y),这样数组可以减小维度,
先预处理出以每个点为左上角的最大地毯,f数组
DP(O(N^2)),或者用二维树状数组(O(N^2 log^2N))
还需要预处理出要覆盖每个点,需要哪些地毯,记录编号,cov数组

问题就变成,对于所有的地毯,枚举铺设哪些,使得在最少块数下覆盖所有的1。

可以做的一个优化就是对于两块可以铺盖的地毯i,j(i,j表示点的编号),如果i可以完全包含j,那么选i一定比比选j差,因为地毯是可以覆盖的,所以选尽量大的总是好的。这样我们搜索时就不需要枚举j了。

h函数:这个函数是要满足一定条件的估价,刘汝佳叔叔称为“乐观估价”,因为当now+h() >= ans时我们会return,那么h的估价一定不能比真实情况大,而且为了保证效果又不能比真实情况小太多。所以h函数一般需要好好想一想。对于这道题不错的一个估价是:当前状态下,遍历一遍所有的点,对于还没有被覆盖的,我们把所有可以覆盖这个点的地毯都铺上,这些地毯算为1块。最后计算出的当前状态下还需要铺的“地毯数”就是h函数返回值。这个结果一定是比真实情况小的,但也不会小很多,性能还是不错的。

之后就可以写搜索了,一开始自己写的还是有问题,之后看了一份题解,换成人家的搜索姿势就A了。[泪]
我的搜索姿势是:当前点t没有被覆盖时,一定要铺一块地毯覆盖这个点,所以就铺t这块地毯,若已经被覆盖,那么就可以不铺,但是还是要搜索铺一块的情况,不过剪枝:如果要再铺一块的话没有任何效果那么就不铺。
别人家的搜索姿势:当前点被覆盖时,不需要再铺,只搜索不铺,若没被覆盖,枚举用哪一块来覆盖(cov数组)。

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;int n, m, ans, f[115], num[115], cov[115][115];bool map[115], vis[115], vis2[115]; int id(int x, int y){    return (x-1)*m+y;}bool cover(int t){    if(!map[t]) return 1;    for(int i = 1; i <= num[t]; i++){        if(vis[cov[t][i]]) return 1;    }    return 0;}int h(){    int res = 0;    for(int i = id(n, m); i; i--){        vis2[i] = vis[i];    }    for(int i = 1; i <= n; i++)    for(int j = 1; j <= m; j++){        int t = id(i, j);        if(cover(t)) continue;        res++;        for(int k = 1; k <= num[t]; k++){            vis[cov[t][k]] = 1;        }    }    for(int i = id(n, m); i; i--){        vis[i] = vis2[i];    }    return res;}void sec(int t, int ed){    if(ed + h() >= ans) return ;    if(t > id(n, m)){        ans = min(ans, ed); return ;    }    if(!map[t]){sec(t+1, ed); return ;}    if(cover(t)){        sec(t+1, ed); return ;    }    for(int i = 1; i <= num[t]; i++){        vis[cov[t][i]] = 1;        sec(t+1, ed+1);        vis[cov[t][i]] = 0;    }}int main(){    while(scanf("%d %d", &m, &n) && n){        memset(  f, 0, sizeof   f);        memset(map, 0, sizeof map);        memset(cov, 0, sizeof cov);        memset(num, 0, sizeof num);        memset(vis, 0, sizeof vis);        for(int i = 1; i <= n; i++)        for(int j = 1; j <= m; j++){            int t = id(i, j);            scanf("%d", map+t);        }        for(int i = n; i; i--){            f[id(i, m)] = map[id(i, m)];            for(int j = m-1; j; j--){                int t = id(i, j); if(map[t])                f[t] = min(f[t+m], min(f[t+1], f[t+m+1])) + 1;            }         }        for(int i = n; i; i--)        for(int j = m; j; j--){            int t = id(i, j);            if(f[t+1] < f[t]) f[t+1] = 0;            if(f[t+m] < f[t]) f[t+m] = 0;            if(f[t+m+1] < f[t]) f[t+m+1] = 0;        }        ans = 0;        for(int i = n; i; i--)        for(int j = m; j; j--){            int t = id(i, j);            if(f[t]) ans++;            for(int k = 0; k < f[t]; k++)            for(int l = 0; l < f[t]; l++){                int t2 = id(i+k, j+l);                cov[t2][++num[t2]] = t;            }        }        sec(1, 0);        printf("%d\n", ans);    }    return 0;}

哎,国庆这些天的培训,每次考试都是被虐,感觉快没脸参加noip了。最后杜神的搜索题感觉都不算很难的样子,但是自己写不出来,考场上想不出来。这几天就先练练搜索了。

2015.10.14
然而,自己yy的DLX代码可以用后就来重写了一下这道题,下面是DLX版本。
不过暂时没加估价函数来剪枝,导致跑的比IDA*要慢一些。(16Ms 与 172Ms)

#include <cstdio>#include <algorithm>#include <cstring>using namespace std;int n, m, Row, Col, f[15][15], id[15][15];bool map[15][15], mtr[105][105];struct DLX{    int sz, ans, ele, col[1005], row[1005], del[1005];    int U[1005], D[1005], L[1005], R[1005], S[105];    void init(int c)    {        ans = 1<<30; sz = ele = c;        memset(U, 0, sizeof U);        memset(D, 0, sizeof D);        memset(L, 0, sizeof L);        memset(R, 0, sizeof R);        memset(S, 0, sizeof S);        memset(del, 0, sizeof del);        for(int i = 0; i <= ele; i++)        {            U[i] = D[i] = i;            L[i] = i-1; R[i] = i+1;            row[i] = i;        }        L[0] = sz; R[sz] = 0;    }    void add_col(int c, bool *co)    {        int fir = sz;        for(int i = 1; i <= ele; i++) if(co[i])        {            sz++; S[i]++;            D[sz] = i; U[sz] = U[i];            D[U[i]] = sz; U[i] = sz;             L[sz] = sz-1; R[sz] = sz+1;            col[sz] = c; row[sz] = i;        }        if(fir != sz) L[fir+1] = sz, R[sz] = fir+1;    }    void remove(int c)      {          int t = c;        do        {            int tt = row[t];            if(!del[tt]) L[R[tt]] = L[tt], R[L[tt]] = R[tt], del[tt] = col[c];            t = R[t];        }   while(t != c);    }      void resume(int c)      {             int t = c;        do        {            int tt = row[t];            if(del[tt] == col[c]) L[R[tt]] = tt, R[L[tt]] = tt, del[tt] = 0;            t = R[t];        }   while(t != c);    }    void dfs(int sum)    {        if(sum  >= ans) return ;        if(!R[0]) { ans = sum; return ;}        int r, Mins = 1<<30;        for(int j = R[0]; j; j = R[j]) if(S[j] < Mins)        {            Mins = S[j], r = j;        }           for(int i = D[r]; i != r; i = D[i])        {            remove(i);            dfs(sum + 1);            resume(i);        }    }    void solve()    {        dfs(0);        if(ans < (1<<30)) printf("%d\n", ans);        else printf("-1\n");    }};int main(){    while(scanf("%d %d", &m, &n) && n && m)    {        memset(  f, 0, sizeof   f);        memset( id, 0, sizeof  id);        memset(map, 0, sizeof map);        memset(mtr, 0, sizeof mtr);        Row = Col = 0;        for(int i = 1; i <= n; i++)        for(int j = 1; j <= m; j++)        {            scanf("%d", map[i]+j);            if(map[i][j]) id[i][j] = ++Row;        }        for(int i = n; i; i--)        for(int j = m; j; j--) if(map[i][j])        {            f[i][j] = min(f[i][j+1], min(f[i+1][j], f[i+1][j+1])) + 1;        }        for(int i = n; i; i--)        for(int j = m; j; j--)        {            if(f[i][j] > f[i+1][j]) f[i+1][j] = 0;            if(f[i][j] > f[i][j+1]) f[i][j+1] = 0;            if(f[i][j] > f[i+1][j+1]) f[i+1][j+1] = 0;        }        for(int i = 1; i <= n; i++)        for(int j = 1; j <= m; j++) if(f[i][j])        {            Col++;            for(int k = i; k-i < f[i][j]; k++)            for(int l = j; l-j < f[i][j]; l++)            {                mtr[Col][id[k][l]] = 1;            }        }        DLX T;        T.init(Row);        for(int i = 1; i <= Col; i++)        {            T.add_col(i, mtr[i]);         }        T.solve();    }    return 0;}
0 0