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;
}


發佈了100 篇原創文章 · 獲贊 2 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章