ACM练级日志:POJ 3740 与Dancing Links

来源:互联网 发布:点击按钮执行php代码 编辑:程序博客网 时间:2024/05/22 07:41

感觉自己搜索能力还是不够,前些日子记得有个地方出过个题,不会做,一查解题报告发现跟个叫什么Dancing Links的玩意有关,于是就去查了查,然后发现这玩意似乎有点碉……刚刚AC掉模板题,做个记录…


Dancing Links,简称DLX,用于给矩阵精确匹配问题的DFS过程大幅度剪枝,甚至具有自动剪枝的作用。所谓矩阵精确匹配问题,就是你有一个矩阵,里面元素不是0就是1,然后希望你选出一些行,使得这些行的总集中,每一列都有且只有一个1. 原来朴素的DFS就是枚举列,然后把这一列删掉——问题就出在这里,“删掉”一列需要很高的时间复杂度,所以往往我们会只把它标记成“删除了”,但是接下来的搜索中,明明矩阵应该越来越小,我们却花着最初时的代价搜索矩阵,明显很亏。


DLX厉害的地方在于它的数据结构:它用一个十字链表结点来表示每一个矩阵中为1的地方,然后它利用双向链表方便的删除以及删除恢复操作:删除x时只需 R[ L[x] ]= R[x] , L[ R[x] ] = L[x] (我的左边的右边是我的右边,反之亦然),  x就找不着了,然后恢复的时候,只需要两句:L[ R[x] ] = x, R[ L[x] ] =x 就又能找着x了。这个对于回溯是非常有用的。


而且DLX解决问题的单一性使它可以作为模板直接使用,前期建模以后,直接构建矩阵然后去DFS就搞定了……所以最麻烦的地方恐怕还是建模。


不管怎样,先放一个刚刚弄的模板吧,只过了POJ 3740, 不知道好不好使。另外momodi的关于DLX的文章非常好懂,代码也很规范,值得一读~


#include<stdio.h>#include<string.h>#include<math.h>using namespace std; const int INF=2<<25;const int MAXNUM=6010; int n,m;int u[MAXNUM], d[MAXNUM], l[MAXNUM], r[MAXNUM];//上下左右int s[MAXNUM], col[MAXNUM];//s[i]:第i列有几个节点,即有几个1,col[i]能告诉你i这个节点在第几列 int head;//总表头,其实就是0号节点int p_nodes;//目前用了多少节点 void del(int c)//删掉列c以及列c中A[i][j]=1的所有行{    l[ r[c] ] = l[c];    r[ l[c] ] = r[c];         for(int i=d[c]; i!=c; i=d[i])    {        for(int j=r[i]; j!=i; j=r[j])        {            u[ d[j] ] = u[j];            d[ u[j] ] = d[j];            s[ col[j] ] --;        }    }    return;} void resume(int c)//恢复上面的操作{    for(int i=u[c]; i!=c; i=u[i])    {        for(int j=l[i]; j!=i; j=l[j])        {            s[ col[j] ] ++;            d[ u[j] ]=j;            u[ d[j] ]=j;        }    }         r[ l[c] ] =c;    l[ r[c] ] = c;    return;} bool DFS(int depth){    ////TEST    //cout<<depth<<endl;         if(r[head] == head)        return true;//矩阵被删干净了         int min1=INF, now;//挑一个1数最少列的先删    for(int t=r[head]; t!=head; t=r[t])    {        if(s[t] <=min1 )        {            min1=s[t];            now=t;        }    }         del(now);//删掉此列    int i, j;    for(i=d[now]; i!=now; i=d[i])    {//枚举这一列每个1由哪行来贡献,这行即为暂时性的答案,如果需记录答案,此时ans[depth]=i        for(j=r[i]; j!=i; j=r[j])        {            del( col[j] );//选取了第i行,就要把本行所有的1所在列都删掉        }                 bool tmp=DFS(depth+1);                 for(j=l[i]; j!=i; j=l[j])        {            resume(col[j]);        }             if(tmp==true)            return true;                          }    resume(now);    return false;} void init(){    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(col,0,sizeof(col));     head=0;    p_nodes=0;} void InitLinks()//注意:该链表两个方向都是循环的,最上面的上面指最下面{    int i,j;    r[head]=1;    l[head]=m;         for(i=1;i<=m;i++)//制作列的表头,使用结点1~m    {        l[i]=i-1;        r[i]=i+1;        if(i==m)            r[i]=head;        u[i]=i;        d[i]=i;        s[i]=0;        col[i]=i;    }         p_nodes=m;    for(i=1;i<=n;i++)    {        int row_first1=-1;//看看这一行是不是已经有第一个1了        for(j=1;j<=m;j++)        {            int k;            scanf("%d", &k);            if(k==1)            {                p_nodes++;                s[j] ++;                u[p_nodes] = u[j];                d[ u[j] ] = p_nodes;                u[j] = p_nodes;                d[p_nodes] = j;                                 col[p_nodes] = j;//和列的关系处理完毕                                 if(row_first1==-1)                {                    l[p_nodes] = r[p_nodes] = p_nodes;                    row_first1=p_nodes;                }                                 else                {                    l[p_nodes] = l[row_first1];                    r[ l[row_first1] ] = p_nodes;                    r[p_nodes] = row_first1;                    l[ row_first1 ]=p_nodes;//和行的关系处理完毕                }            }        }    }} int main(){    while(scanf("%d %d", &n, &m) ==2)    {        init();        InitLinks();        bool ok=true;        for(int i=1;i<=m;i++)        {            if(s[i]==0)            {                ok=false;                break;            }        }        if(ok)            ok=DFS(1);                 if(ok)            printf("Yes, I found it\n");        else            printf("It is impossible\n");    }    return 0;}


0 0
原创粉丝点击