POJ3740-精确覆盖,Dancing_links

位运算裸搜版(500ms):

#include <cstdio>
#include <cstring>
#include <iostream>
#define LL long long
using namespace std;

const int N=32;
const int L=10;

LL  w[L],goal[L],x[20][L];
int m,n,a[20][320];

inline bool cont(LL *x,LL *y)
{
    for (int i=0; i<m; i++)
    {
        if (x[i/N]&(1<<(i%N)) && y[i/N]&(1<<(i%N))) return true;
    }
    return false;
}

bool dfs(int i)
{
    bool flag=true;
    for (int k=0; k<L && flag; k++) if (goal[k]!=w[k]) flag=false;
    if (flag) return true;
    if (i>n) return false;
    if (cont(w,x[i])) return dfs(i+1);

    LL tmp[L];
    for (int k=0; k<L; k++) tmp[k]=w[k],w[k]+=x[i][k];
    if (dfs(i+1)) return true;
    for (int k=0; k<L; k++) w[k]=tmp[k];
    if (dfs(i+1)) return true;

    return false;
}
int main()
{
    while (~scanf("%d%d",&n,&m))
    {
        memset(x,0,sizeof(x));
        for (int i=1; i<=n; i++)
        {
            for (int j=0; j<m; j++)
            {
                scanf("%d",&a[i][j]);
                if (a[i][j]) x[i][j/N]|=1<<(j%N);
            }
        }

        memset(goal,0,sizeof(goal));
        for (int j=0; j<m; j++) goal[j/N]|=1<<(j%N);
        memset(w,0,sizeof(w));
        if (dfs(1)) printf("Yes, I found it\n");
        else        printf("It is impossible\n");
    }
    return 0;
}

Dance Links版(63ms):

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

const int NN=6000;
const int INF=10000000;

int n,m,cnt,head,L[NN],R[NN],U[NN],D[NN],S[NN],C[NN],H[NN];

inline void add_link(int i,int j)
{
    C[++cnt]=j;
    S[j]++;

    D[cnt]=j;
    U[cnt]=U[j];
    if (H[i]) R[cnt]=H[i],L[cnt]=L[H[i]];
    else      R[cnt]=L[cnt]=cnt;
    H[i]=cnt;

    U[D[cnt]]=cnt;
    D[U[cnt]]=cnt;
    R[L[cnt]]=cnt;
    L[R[cnt]]=cnt;
}

void remove(int c)//删除第c列上的元素所在的行
{
    R[L[c]]=R[c];
    L[R[c]]=L[c];
    //删除c,c是列指针,删除了c就代表删除了整列,因为递归后不可能访问到c了

    for (int i=D[c]; i!=c; i=D[i]) //c所在列上的元素i
        for (int j=R[i]; j!=i; j=R[j]) //i和j一行的,删掉j
        {
            U[D[j]]=U[j];
            D[U[j]]=D[j];
            S[C[j]]--;//j所在列的元素(‘1’的个数)-1;
        }
}

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]]=j;
            U[D[j]]=j;
            S[C[j]]++;
        }
    L[R[c]]=R[L[c]]=c;
}

bool dance()
{
    if (R[head]==head)//列指针删完了,任务完成
    {
        puts("Yes, I found it");
        return true;
    }

    int s(INF),c;
    for (int i=R[head]; i!=head; i=R[i]) if (S[i]<s) s=S[i],c=i;
    //找元素最少的列c,一种优化

    remove(c);//删除列c
    for (int i=D[c]; i!=c; i=D[i])
    {
        for (int j=R[i]; j!=i; j=R[j]) remove(C[j]);//删除所有可能的冲突元素
        if (dance()) return true;
        for (int j=L[i]; j!=i; j=L[j]) resume(C[j]);
    }
    resume(c);

    return false;
}

int main()
{
    int c;
    head=0;
    while (~scanf("%d%d",&n,&m))
    {
        cnt=m;
        for (int i=0; i<=m; i++)
        {
            C[i]=U[i]=D[i]=i;
            //这里C[i]没有赋值时也过了,因为不会有对列指针取列标号的操作
            L[i+1]=i;
            R[i]=i+1;
            S[i]=0;
        }
        L[0]=m; R[m]=0; //初始化十字链表

        for (int i=1; i<=n; i++)
        {
            H[i]=0;
            for (int j=1; j<=m; j++)
            {
                c=getchar();
                while (!isdigit(c)) c=getchar();
                if (c=='1') add_link(i,j);
            }
        }

        if (!dance()) puts("It is impossible");
    }
    return 0;
}

你可能感兴趣的:(POJ3740-精确覆盖,Dancing_links)