POJ1084-重复覆盖,DLX

这题可理解为用最少用多少火柴棒覆盖全部的正方形,以正方形为列,火柴棒为行,重复覆盖模型明显。

建图的时候要找出所有正方形所包含的火柴棒,我是这样找的:

先确定最左上边的正方形(边长为1~n)所包含的边,因为对于等大的两个正方形,其相同位置的火柴棒边的标号的差是一定的,所以算出一个正方形的边根据差就能得出其他的等大的正方形的边了。

/*
第一道像样一点的DLX重复覆盖,这题数据弱~
*/
#include <cstdio>
#include <cstring>
#include <vector>
#include <iostream>
using namespace std;

const int NN=100;
const int MM=10000;
const int a[7]={0,1,2,6,15,31,56};
const int lim[6]={0,1,3,6,9,14};

vector<int> have[NN];
int n,ans,cnt,U[MM],D[MM],L[MM],R[MM],C[MM],H[MM],V[NN],S[NN],hash[NN];
bool destr[NN];

void init(int rows,int columns)
{
    int i;
    for (i=0; i<=columns; i++)
    {
        C[i]=U[i]=D[i]=i;
        L[i+1]=i;
        R[i]=i+1;
        S[i]=0;
    }
    cnt=L[0]=columns; R[columns]=0;

    for (i=1; i<=rows; i++) V[i]=0;
}

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

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

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

void build()
{
    int i,j,k,o,l,t,size;
    bool flag;
    int N=n+n+1;
    for (i=0; i<=a[n+1]-1; i++) have[i].clear();
    for (t=1; t<=n; t++)
    {
        l=n-t+1;
        k=a[t];
        for (i=1; i<=l; i++)              have[k].push_back(i);   //up
        for (j=1,i=n+1; j<=l; i+=N,j++)   have[k].push_back(i);   //left
        for (j=1,i=n+1+l; j<=l; i+=N,j++) have[k].push_back(i);   //right
        for (j=1,i=N*l+1; j<=l; i++,j++)  have[k].push_back(i);   //down

        for (i=0; i<t; i++)
          for (j=0; j<t; j++)
          {
              if (i==0 && j==0) continue;
              k++;
              for (o=0; o<4*l; o++) have[k].push_back(have[a[t]][o]+i*N+j);
          }
    }

    init(2*n*(n+1),a[n+1]-1);
    for (i=1; i<=a[n+1]-1; i++)
    {
        size=have[i].size();
        flag=true;
        for (j=0; j<size && flag; j++) if (destr[have[i][j]]) flag=false;
        if (!flag)
        {
            R[L[i]]=R[i];
            L[R[i]]=L[i];
            continue;
        }
        for (j=0; j<size; j++) link(have[i][j],i);
    }
}

int h() //启发函数
{
    int i,j,k,ret=0;
    memset(hash,0,sizeof(hash));
    for (i=R[0]; i; i=R[i])
    {
        if (hash[C[i]]) continue;
        ret++;
        hash[C[i]]=1;
        for (j=D[i]; j!=i; j=D[j])
          for (k=R[j]; k!=j; k=R[k]) hash[C[k]]=1;
    }
    return ret;
}

inline void remove(int c)
{
    for (int i=D[c]; i!=c; i=D[i])
    {
        L[R[i]]=L[i];
        R[L[i]]=R[i];
    }
}

inline void resume(int c)
{
    for (int i=U[c]; i!=c; i=U[i])
    {
        R[L[i]]=i;
        L[R[i]]=i;
    }
}

void dance(int k)
{
    if (k+h()>=ans) return;
    if (!R[0])
    {
        if (k<ans) ans=k;
        return;
    }

    int i,j,c,tmp=MM;
    for (i=R[0]; i; i=R[i]) if (S[i]<tmp) tmp=S[c=i];

    for (i=D[c]; i!=c; i=D[i])
    {
        remove(i);
        for (j=R[i]; j!=i; j=R[j]) remove(j);
        dance(k+1);
        for (j=L[i]; j!=i; j=L[j]) resume(j);
        resume(i);
    }
}

int main()
{
    int T,k,x;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d%d",&n,&k);
        if (k==0) { printf("%d\n",lim[n]); continue; }

        memset(destr,0,sizeof(destr));
        for (int i=1; i<=k; i++)
        {
            scanf("%d",&x);
            destr[x]=1;
        }
        build();
        ans=lim[n];
        dance(0);
        printf("%d\n",ans);
    }
    return 0;
}


你可能感兴趣的:(POJ1084-重复覆盖,DLX)