spoj NQUEEN N皇后 DLX

     经典N皇后问题,本来是做POJ3239的,结果写完了才发现n最大300..DLX一样会超时,而且那题居然是构造做的...spoj上n最多只有50,而且初始会给出若干个已有皇后的位置。建表时可以建成n*n行*(6*n-2)列的01矩阵,其中行表示每个格子的位置,列的话,前n列n是第i行,下n列是第j列,在下面2*n-1列是左斜线,最后2*n-1列是又斜线。每行恰好有4个1。然后就是去搜了..这题有一点要注意,就是结束状态时,行列一定是完全覆盖的,但是左斜线右斜线不一定会完全覆盖。所以每一次搜索时,找第一个位置时要有一点变化,具体实现看代码吧,在这里还坑了挺长时间的,不过A掉这题后,感觉已经完全明白了DLX是怎么回事了...

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
typedef long long ll;
const int tt=60;
const int maxr=tt*tt;
const int maxc=6*tt;
const int maxn=maxr*maxc;
int col[maxn],row[maxn],R[maxn],L[maxn],U[maxn],D[maxn];
int ct[10];
int a[55];

int head[tt][tt];

int S[maxr];
int size,mRow;
int n,m,k;
struct ANS
{
    int x,y;
}ans[maxr];

int anss[330];
int st[maxr];

struct DLX
{
    void resume(int c)
    {
        L[R[c]]=c;
        R[L[c]]=c;
        for (int i=U[c]; i!=c; i=U[i])
        for (int j=L[i]; j!=i; j=L[j])
        {
            U[D[j]]=j;
            D[U[j]]=j;
            ++S[col[j]];
        }
    }
    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])
        {
            U[D[j]]=U[j];
            D[U[j]]=D[j];
            --S[col[j]];
        }
    }
    void init(int m)
    {
        for (int i=1; i<=m; i++)
        {
            L[i]=i-1;
            R[i]=i+1;
            U[i]=D[i]=i;
            S[i]=0;
            col[i]=i;
            row[i]=0;
        }
        L[0]=m;
        R[0]=1;
        R[m]=0;
        U[0]=D[0]=0;
        size=m;
        mRow=0;
    }
    int makehead(int c)
    {
        size++;
        S[c]++;
        col[size]=c;
        row[size]=mRow;
        L[size]=R[size]=size;

        U[size]=c;
        D[size]=D[c];
        U[D[size]]=D[U[size]]=size;
        return size;
    }
    void addcol(int id,int c)
    {
        size++;
        S[c]++;
        col[size]=c;
        row[size]=mRow;

        L[size]=id;
        R[size]=R[id];
        L[R[size]]=R[L[size]]=size;

        U[size]=c;
        D[size]=D[c];
        U[D[size]]=D[U[size]]=size;
    }
    void addrow(int i,int j)
    {
        int id;
        mRow++;
        ans[mRow].x=i;
        ans[mRow].y=j;
        id=makehead(i+ct[0]);
        head[i][j]=id;
        addcol(id,j+ct[1]);
        addcol(id,i+j+ct[2]);
        addcol(id,i-j+n-1+ct[3]);
    }
    bool dfs(int k)
    {
        if (k==n)
        {
            for (int i=0; i<k; i++)
            if (a[ans[st[i]].x]==0)anss[ans[st[i]].x]=ans[st[i]].y;
            return true;
        }
        int c;
        int minn=(1<<28);
        for (int i=R[0]; i<=n; i=R[i])
        {
            if (!S[i]) return false;
            if (S[i]<minn)
            {
                minn=S[i];
                c=i;
            }
        }
        remove(c);
        for (int i=D[c]; i!=c; i=D[i])
        {
            st[k]=row[i];
            for (int j=R[i]; j!=i; j=R[j]) remove(col[j]);
            if (dfs(k+1)) return true;
            for (int j=L[i]; j!=i; j=L[j]) resume(col[j]);
        }
        resume(c);
        return false;
    }
}dlx;
int main()
{

    while(~scanf("%d",&n))
    {
        for (int i=0; i<n; i++)
        scanf("%d",&a[i]);
        ct[0]=1;
        ct[1]=n+1;
        ct[2]=(n<<1)+1;
        ct[3]=(n<<2);

        dlx.init(6*n-2);
        for (int i=0; i<n; i++)
         for (int j=0; j<n; j++)
         {
             dlx.addrow(i,j);
         }
         int k=0;
         for (int i=0; i<n; i++)
         {
             if (a[i])
             {
                 k++;
                 int x=head[i][a[i]-1];
                 anss[i]=a[i]-1;
                 dlx.remove(col[x]);
                 for (int i=R[x]; i!=x; i=R[i]) dlx.remove(col[i]);
             }
         }
        if (dlx.dfs(k))
        {
            cout<<anss[0]+1;
            for (int i=1; i<n; i++)
            cout<<" "<<anss[i]+1;
            cout<<endl;
        }
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值