dancing link 解决八皇后

http://blog.sina.com.cn/s/blog_51cea4040100gwqw.html  八皇后  dancing link 


 话说,N皇后把ccy折磨了N久,写了x个版本,结果都是每每writing结束后,猛然发现,这种想法又有漏洞,然后Ctrl+A再Delete,晕……o(╯□╰)o……

    网上有看到说N皇后问题是个重复覆盖问题,于是,ccy朝这个方向想了N久,然后,提出了2个特点,实在不觉得重复覆盖模型可以解决这个N皇后。下面,让ccy把这几个问题摆出来。

    我是这样建的模型,把行当做皇后可放的格子,把列当做所有格子。那么,一个行,既在某个地方放下一个皇后会影响哪些格子。然后,做重复覆盖问题。接着,就囧掉了。怎么重复覆盖?选一些行,使某些列有1,明显不对呀!为什么不对?

    1、我们要选取一些行,而行数是定了的,为N行。

    2、棋盘里的格子被覆盖有两种情况,一是格子上放的皇后,那么,就不能有其他皇后再影响这格,二是格子被覆盖是因为其他位置的皇后影响了,这样,就可以有其他皇后再影响这个位置。

    有了这两个性质,ccy就很纠结地发现,我没有办法用重复覆盖或者精确覆盖来解决。希望有大牛来帮帮忙,指条明路,O(∩_∩)O谢谢~

**************************************************************************************************

http://www.spoj.pl/problems/NQUEEN/

1771. Yet Another N-Queen Problem
Problem code: NQUEEN


After solving Solution to the n Queens Puzzle by constructing, LoadingTime wants to solve a harder version of the N-Queen Problem. Some queens have been set on particular locations on the board in this problem. Can you help him??

Input
The input contains multiple test cases. Every line begins with an integer N (N<=50), then N integers followed, representing the column number of the queen in each rows. If the number is 0, it means no queen has been set on this row. You can assume there is at least one solution.

Output
For each test case, print a line consists of N numbers separated by spaces, representing the column number of the queen in each row. If there are more than one answer, print any one of them.

Example
Input:
4 0 0 0 0
8 2 0 0 0 4 0 0 0

Output:
2 4 1 3
2 6 1 7 4 8 3 5

**************************************************************************************************

题目大意,一个N*N的棋盘中,有些皇后已经放下,求一个可行性方案。

**************************************************************************************************

    话说,ccy首先做的事是老老实实地写过米字链,主要是因为,刚接触DLx,于是就傻乎乎地单单用了所谓的Links结构而已。然后,当时,写的是求所有解,测了个N=30的,发觉很没效率,就抛弃它了。

    但是,就刚刚,ccy改了下,求一解的情况,然后交了下代码,然后,囧掉。居然,比我第一次交的DLX算法还快,内牛满面!!!!

    其实,关于这个米字链,我觉得大家可以无视它的,真的,偏偏它还最有速度。

    棋盘上的行对应行,棋盘上的列对应列,然后,没有放皇后的地方标号为1,放了皇后的地方,就删除它和它可影响的地方。

    于是乎,这就是个非常单纯的暴力搜索,只是在处理矛盾的时候,是采用链表来删除而已,保证每次我们从矩阵中取出的1的格子都与前面所取的不冲突。

    至于代码,如果今天天气特别好,偏偏你又不想出去晒太阳吹河风,那么,就看ccy这个很无敌的代码吧!!

    (看了一下,也没得好长,就200多点点而已,本来以为很长很长很长的!!(⊙o⊙)…)

【搜索】Dancing <wbr>Links——N皇后问题


#include<iostream>
using namespace std;

const int maxn=55;

int L[maxn*maxn],R[maxn*maxn],U[maxn*maxn],D[maxn*maxn],AL[maxn*maxn],AR[maxn*maxn],BL[maxn*maxn],BR[maxn*maxn];
int Cd[maxn*maxn],Rd[maxn*maxn],Ad[maxn*maxn],Bd[maxn*maxn];
int map[maxn][maxn];
int nRow[maxn*maxn],nCol[maxn*maxn];
int S[maxn];
int n,cnt,head;
int stack[maxn*maxn*2],tot;

int total;
int ans[maxn];
bool ans_get;

void Ins_cnt(int x)
{
    L[x]=R[x]=U[x]=D[x]=AL[x]=AR[x]=BL[x]=BR[x]=x;
}

void Ins_Column(int x)
{
    cnt++;

    Ins_cnt(cnt);

    L[R[head]]=cnt;
    R[cnt]=R[head];
    L[cnt]=head;
    R[head]=cnt;

    Cd[x]=cnt;
}

void Ins_Row(int x)
{
    cnt++;
    Ins_cnt(cnt);
    Rd[x]=cnt;
}

void Ins_A(int x)
{
    cnt++;
    Ins_cnt(cnt);
    Ad[x]=cnt;
}

void Ins_B(int x)
{
    cnt++;
    Ins_cnt(cnt);
    Bd[x]=cnt;
}

void Ins(int cnt,int head,int L[],int R[])
{
    L[R[head]]=cnt;
    R[cnt]=R[head];
    L[cnt]=head;
    R[head]=cnt;
}

void Ins_node(int r,int c)
{
    int hd;

    cnt++;
    map[r][c]=cnt;

    hd=Cd[c];
    Ins(cnt,hd,U,D);
    hd=Rd[r];
    Ins(cnt,hd,L,R);
    hd=Ad[r+c-1];
    Ins(cnt,hd,AL,AR);
    hd=Bd[n-r+c];
    Ins(cnt,hd,BL,BR);

    nRow[cnt]=r;
    nCol[cnt]=c;
}

void init()
{
    head=0;
    Ins_cnt(head);
    cnt=0;
    tot=0;
    stack[tot]=-1;

    for (int i=1;i<=n;i++)
    {
        S[i]=n;
        Ins_Column(i);
    }
    for (int i=1;i<=n;i++)
        Ins_Row(i);
    for (int i=1;i<2*n;i++)
        Ins_A(i);
    for (int i=1;i<2*n;i++)
        Ins_B(i);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
            Ins_node(i,j);
}

void move(int x)
{
    if (!nRow[x]) return;
    tot++;
    stack[tot]=x;
    L[R[x]]=L[x]; R[L[x]]=R[x];
    U[D[x]]=U[x]; D[U[x]]=D[x];
    AL[AR[x]]=AL[x]; AR[AL[x]]=AR[x];
    BL[BR[x]]=BL[x]; BR[BL[x]]=BR[x];
    S[nCol[x]]--;
}

void Remove(int x)
{
    for (int i=D[x];i!=x;i=D[i])
        move(i);
    for (int i=L[x];i!=x;i=L[i])
        move(i);
    for (int i=AL[x];i!=x;i=AL[i])
        move(i);
    for (int i=BL[x];i!=x;i=BL[i])
        move(i);
    move(x);
    tot++;
    stack[tot]=-1;
}

void sume(int x)
{
    if (!nRow[x]) return;
    S[nCol[x]]++;
    L[R[x]]=R[L[x]]=U[D[x]]=D[U[x]]=AL[AR[x]]=AR[AL[x]]=BL[BR[x]]=BR[BL[x]]=x;
}

void Resume()
{
    tot--;
    while (stack[tot]!=-1)
    {
        sume(stack[tot]);
        tot--;
    }
}

void dfs()
{
    if (ans_get) return;

    if (R[head]==head)
    {
        ans_get=true;
        printf("%d",ans[1]);
        for (int i=2;i<=n;i++)
            printf(" %d",ans[i]);
        printf("\n");
        return;
    }

    int minnum=INT_MAX;
    int c;
    for (int i=R[head];i!=head;i=R[i])
    {
        if (!ans[nCol[D[i]]] && !S[nCol[D[i]]]) return;
        if (S[nCol[D[i]]]<minnum)
        {
            minnum=S[nCol[D[i]]];
            c=i;
        }
    }

    R[L[c]]=R[c];
    L[R[c]]=L[c];
    for (int i=D[c];i!=c;i=D[i])
    {
        ans[nRow[i]]=nCol[i];
        Remove(i);
        dfs();
        if (ans_get) return;
        Resume();
    }
    R[L[c]]=L[R[c]]=c;
}

int main()
{
    freopen("data.in","r",stdin);
    freopen("data.out","w",stdout);

while (scanf("%d",&n)!=EOF)
{
    memset(nRow,0,sizeof(nRow));
    memset(nCol,0,sizeof(nCol));
    init();
    for (int i=1;i<=n;i++)
    {
        int j;
        scanf("%d",&j);
        if (j)
        {
            R[L[j]]=R[j];
            L[R[j]]=L[j];
            ans[i]=j;
            Remove(map[i][j]);
        }
    }
    ans_get=false;
    dfs();
}

    fclose(stdin);
    fclose(stdout);
    return 0;
}
**************************************************************************************************

    下面,是这文的重头戏,Dancing Links。

 

    对于任意一格,如果我们放了一个皇后,那么,这个格子所在的行,列,左斜线,右斜线都不能再放皇后了。那么,对于一格而言,实际上它影响的就正是这四个东西:行,列,左斜线,右斜线。那么,我们可以定义这四个东东为列,定义每个格子为行。这样问题就转换成一个精确覆盖了。

    我们拿棋盘的a行(在我们构造的图中为b列)来说吧。如果,我选取了b列,那么其他会影响b列的格子,必然是在棋盘中的a行上,那么就不能再选取。所以,这题就巧妙地转换为一个精确覆盖的模型了。

 

    在一个N*N的棋盘中,构造的图的行为N*N。

    列为:棋盘中的行数+棋盘中的列数+棋盘中的左斜线数+棋盘中的右斜线数=N+N+2*N-1+2*N-2=6*N-2。

 

    构好图后,就是很基础滴01矩阵精确覆盖模型了。

【搜索】Dancing <wbr>Links——N皇后问题

#include<iostream>
using namespace std;

const int maxn=55;
const int maxc=55*6;
const int maxr=55*55;
const int maxt=maxc+maxr*4;

int L[maxt],R[maxt],U[maxt],D[maxt];
int S[maxc];
int nCol[maxt],nRow[maxt];
int head[maxn][maxn];

int n,cnt;
int a[maxn];
bool used[maxr];
bool ans_get;

bool first_check()
{
    for (int i=1;i<n;i++)
        for (int j=i+1;j<=n;j++)
            if (a[i] && a[j])
                if (a[i]==a[j] || i+a[i]==j+a[j] || i-a[i]==j-a[j]) return false;
    return true;
}

void Ins_node(int cnt,int c)
{
    U[D[c]]=cnt;
    D[cnt]=D[c];
    U[cnt]=c;
    D[c]=cnt;
    S[c]++;
    nCol[cnt]=c;
}

void Remove(int c)
{
    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])
        {
            D[U[j]]=D[j];
            U[D[j]]=U[j];
            S[nCol[j]]--;
        }
}

void Resume(int c)
{
    for (int i=U[c];i!=c;i=U[i])
        for (int j=L[i];j!=i;j=L[j])
        {
            D[U[j]]=U[D[j]]=j;
            S[nCol[j]]++;
        }
    L[R[c]]=R[L[c]]=c;
}

void dfs(int k)
{
    if (k>n)
    {
        ans_get=true;
        return;
    }

    int c,minnum=INT_MAX;
    for (int i=R[0];i<=n;i=R[i])
    {
        if (!S[i]) return;
        if (S[i]<minnum)
        {
            minnum=S[i];
            c=i;
        }
    }

    Remove(c);
    for (int i=U[c];i!=c;i=U[i])
    {
        used[nRow[i]]=1;
        for (int j=R[i];j!=i;j=R[j])
            Remove(nCol[j]);
        dfs(k+1);
        if (ans_get) return;
        used[nRow[i]]=0;
        for (int j=L[i];j!=i;j=L[j])
            Resume(nCol[j]);
    }
    Resume(c);
}

int main()
{
    freopen("data.in","r",stdin);
    freopen("data.out","w",stdout);

    while (scanf("%d",&n)!=EOF)
    {
        for (int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for (int i=0;i<=n*6-2;i++)
        {
            S[i]=0;
            L[i]=i-1; R[i]=i+1;
            U[i]=D[i]=i;
        }
        L[0]=n*6-2; R[n*6-2]=0;

        ans_get=false;
        if (!first_check())
        {
            printf("-1\n");
            continue;
        }

        cnt=n*6-2;
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
            {
                for (int k=1;k<=4;k++)
                {
                    L[cnt+k]=cnt+k-1;
                    R[cnt+k]=cnt+k+1;
                    nRow[cnt+k]=(i-1)*n+j;
                }
                L[cnt+1]=cnt+4;
                R[cnt+4]=cnt+1;
                Ins_node(cnt+1,i);
                Ins_node(cnt+2,n+j);
                Ins_node(cnt+3,n*2+i+j-1);
                Ins_node(cnt+4,n*5+i-j-1);
                head[i][j]=cnt+1;
                cnt+=4;
            }

        int k=0;
        for (int i=1;i<=n;i++)
            if (a[i])
            {
                k++;
                Remove(nCol[head[i][a[i]]]);
                for (int j=R[head[i][a[i]]];j!=head[i][a[i]];j=R[j])
                    Remove(nCol[j]);
            }

        memset(used,0,sizeof(used));
        dfs(k+1);

        if (!ans_get)
            printf("-1");
        else
        {
            for (int i=1;i<=n;i++)
                for (int j=1;j<=n;j++)
                    if (used[(i-1)*n+j]) a[i]=j;
            printf("%d",a[1]);
            for (int i=2;i<=n;i++)
                printf(" %d",a[i]);
        }
        printf("\n");
    }

    fclose(stdin);
    fclose(stdout);
    return 0;
}

    这代码写得很清楚了,ccy觉得。

    里面唯一纠结了一下的,就是我用红色涂了的地方。一般,我们在这个地方找c的时候,用的判断语句都是i!=0,而这里是i<=n,想一下n的列表示的是些什么?n列表示的是棋盘中的行,行所影响的就能覆盖了所有的棋盘,不需要做后面的。

    如果,我们写i!=0,会出错。一些斜线本来就有可能这线上不需要放皇后,也没有皇后可以影响它,所以,如果i>2*n,且S[i]==0,并不表示就无解了。

    其实,最开始ccy参照隋清宇的代码,他写的是i<=n*2,我猜想是这个原因。因为N皇后每行每列都是必放的,这里也起一个判断可行性的作用,所以就是2*n。但是,实验效果证明,n还好些。

    这是2*n的时间:

【搜索】Dancing <wbr>Links——N皇后问题
**************************************************************************************************

    鉴于这个Dancing Links在处理影响时是影响的整列整行整斜线,ccy想到了位运算做N皇后问题,于是乎就写了个。

    我在处理确定的皇后位置时是找到它最上面的那个斜线格子,并且作标记,看代码吧,很明显,但是,成功地超时了。

【搜索】Dancing <wbr>Links——N皇后问题
    ccy个人觉得,位运算在做N<=13的时候确实快,是快在它在判断冲突的时候,是采用数学上的运算一步判断,但是,本身并没有减小搜索树的结点。

    而Dancing Links因为有启发式搜索,所以,它是减小了搜索树的结点。

    在N较小的时候,搜索树本身就小,所以DLX的优势没有体现出来,而N比较大时,树的结点就变得很多了,这个时候,DLX的优点就体现出来啦。

    额,虽然超时了,但是还是贴下代码啦,就当练习位运算好哒!!!!!!

#include<iostream>
using namespace std;

const int maxn=55;

long long n;
long long a[maxn];
long long lmust[maxn],rmust[maxn],w[maxn];
bool put[maxn];
long long upperlimit;

int find_w(int num)
{
    int l=1,r=n;
    int mid;
    while (1==1)
    {
        mid=(l+r)>>1;
        if (num==w[mid]) return mid;
        if (num<w[mid]) r=mid-1;
        else l=mid+1;
    }
}

bool dfs(int k,long long row,long long ld,long long rd)
{
    if (k>n)
    {
        if (row==upperlimit) return true;
        else return false;
    }

    if (lmust[k]) ld|=lmust[k];
    if (rmust[k]) rd|=rmust[k];
    if (put[k]) return dfs(k+1,row,(ld<<1)&upperlimit,rd>>1);

    long long pos=upperlimit&(~(row|ld|rd));
    while (pos!=0)
    {
        long long p=pos&(-pos);
        pos-=p;
        a[k]=find_w(p);
        if (dfs(k+1,row|p,((ld|p)<<1)&upperlimit,(rd|p)>>1)) return true;
    }
    return false;
}

int main()
{
    freopen("data.in","r",stdin);
    freopen("data.out","w",stdout);

    while (scanf("%I64d",&n)!=EOF)
    {
        for (int i=1;i<=n;i++)
            scanf("%I64d",&a[i]);

        upperlimit=(1<<n)-1;

        memset(lmust,0,sizeof(lmust));
        memset(rmust,0,sizeof(rmust));
        memset(put,0,sizeof(put));
        long long row=0;
        for (int i=1;i<=n;i++)
            if (a[i])
            {
                put[i]=1;
                row|=(1<<(a[i]-1));
                int r,c;
                r=i; c=a[i];
                while (r>1 && c>1) r--,c--;
                lmust[r]|=(1<<(c-1));
                r=i; c=a[i];
                while (r>1 && c<n) r--,c++;
                rmust[r]|=(1<<(c-1));
            }

        for (int i=1;i<=n;i++)
            w[i]=(1<<(i-1));

        if (!dfs(1,row,0,0))
        {
            printf("-1\n");
            continue;
        }

        printf("%I64d",a[1]);
        for (int i=2;i<=n;i++)
            printf(" %I64d",a[i]);
        printf("\n");
    }

    fclose(stdin);
    fclose(stdout);
    return 0;
}
**************************************************************************************************

    啦啦啦,合掌,微笑!!!!!撒花花!!!!!!!!

    ccy于x天折磨,终于在N皇后上修得正果。

    - -!

    ~~~~~~~~~~~~~~~~~~~~~~O(∩_∩)O哈哈~~~~~~~~~~~~~~


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值