首先给一个传送门,是我写的精确覆盖问题,也是DLX。
http://blog.csdn.net/vmurder/article/details/40586647
然后再给一个传送门,是这道题的POJ位置。
http://poj.org/problem?id=1084
然后开始说正事。
题意:
多组数据,第一个数是数据个数。
题意就是给出一个n*n的大网格,然后它由一系列的火柴拼成,题中给的图片是第二组测试数据。
现在我们已经删掉了若干个火柴,问至少还需要删掉多少火柴,才能使图中的若干个正方形全部被破坏。
解析:
每个正方形都需要有其上的至少一根火柴被选,那么就转化成了DLX模型。每行是一跟火柴,每列是一个正方形,当然正方形个数是<=5*5+4*4+3*3+2*2+1*1=55的,然后显然若某行这根火柴在某列的正方形中,则new一个节点。这样就可以直接套DLX模版(重复覆盖的模版)求解了。
题解:
跟简单覆盖基本一样,但是深刻理解一下就可以知道,简单覆盖时删除操作是先删列,然后枚举行,跟行有关的列也删掉,每深一层都是经过两次(好吧,你说是一次+for(若干次)我也同意)删除操作。
而为什么要删除列呢?可以理解为剪枝,行呢?用过了。那跟行中元素有关的列呢?既然是精确覆盖,就不能有重复,那么自然就都删掉了,同时,“跟行中元素有关的列”中的元素的所在行因为避免重复就也被删掉了。
而这个重复覆盖问题因为不需要“精确”,所以“跟行中元素有关的列”中的元素虽然需要删,可是这些元素(节点)的所在行,也就是这些根火柴,就不需要删了!
这道题的精髓说完了,来说些糟粕吧,就是根本不需要会,但是不会却不能A的烦人细节。
这么烦人我为什么要说?直接上代码!(火柴与方格间的连接及顺序等等,不用自己想,直接把我的复制过去就行了!)
inline void build()
{
init();
int i,j,k,n,m;
int nmrx=0,npon=0;
scanf("%d%d",&n,&m);
memset(destroy,0,sizeof(destroy));
memset(map,0,sizeof(map));
for(k=0;k<n;k++)for(i=1;i+k<=n;i++)for(j=1;j+k<=n;j++)id[k][i][j]=++nmrx;
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)eid[0][i][j]=++npon;
for(j=1;j<=n+1;j++)eid[1][i][j]=++npon;
}
for(i=1;i<=n;i++)eid[0][n+1][i]=++npon;
for(k=0;k<n;k++)
for(i=1;i+k<=n;i++)
for(j=1;j+k<=n;j++)
{
int A=id[k][i][j],temp;
for(temp=j;temp<=j+k;temp++)map[A][eid[0][i][temp]]=map[A][eid[0][i+k+1][temp]]=1;
for(temp=i;temp<=i+k;temp++)map[A][eid[1][temp][j]]=map[A][eid[1][temp][j+k+1]]=1;
}
for(i=1;i<=m;i++)
{
scanf("%d",&k);
for(j=1;j<=nmrx;j++)if(map[j][k])destroy[j]=1;
}
for(i=1;i<=nmrx;i++)if(!destroy[i])
{
U[i]=D[i]=i;
L[i]=L[0],R[i]=0;
L[0]=R[L[0]]=i;
}
cnt=nmrx;
for(i=1;i<=nmrx;i++)
if(!destroy[i])
for(j=1;j<=npon;j++)
if(map[i][j])
newnode(j,i);
}
真是恶心。DLX早早就写好了,这个乱七八糟的东西因为三次漏题重写了四次。
快来粘一发,然后快速水过此题吧。
好了,贴完整代码。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 60//55个格子
#define M 70//60个火柴
#define NN 300//每个格子4根火柴,算上辅助是5
#define inf 0x3f3f3f3f
using namespace std;
/*60火柴,25格*/
int ans;
struct DLX
{
int U[NN],D[NN],L[NN],R[NN],C[NN];
int H[M],T[N],cnt;
inline void init()
{
cnt=0;
memset(U,0,sizeof(U));
memset(D,0,sizeof(D));
memset(L,0,sizeof(L));
memset(R,0,sizeof(R));
memset(C,0,sizeof(C));
memset(H,0,sizeof(H));
memset(T,0,sizeof(T));
}
inline void newnode(int x,int y)
{
C[++cnt]=y;T[y]++;
if(!H[x])H[x]=L[cnt]=R[cnt]=cnt;
else L[cnt]=H[x],R[cnt]=R[H[x]];
R[H[x]]=L[R[H[x]]]=cnt,H[x]=cnt;
U[cnt]=U[y],D[cnt]=y;
U[y]=D[U[y]]=cnt;
}
int id[N][N][5],eid[2][7][7];
bool destroy[N],map[N][M];
inline void build()
{
init();
int i,j,k,n,m;
int nmrx=0,npon=0;
scanf("%d%d",&n,&m);
memset(destroy,0,sizeof(destroy));
memset(map,0,sizeof(map));
for(k=0;k<n;k++)for(i=1;i+k<=n;i++)for(j=1;j+k<=n;j++)id[k][i][j]=++nmrx;
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)eid[0][i][j]=++npon;
for(j=1;j<=n+1;j++)eid[1][i][j]=++npon;
}
for(i=1;i<=n;i++)eid[0][n+1][i]=++npon;
for(k=0;k<n;k++)
for(i=1;i+k<=n;i++)
for(j=1;j+k<=n;j++)
{
int A=id[k][i][j],temp;
for(temp=j;temp<=j+k;temp++)map[A][eid[0][i][temp]]=map[A][eid[0][i+k+1][temp]]=1;
for(temp=i;temp<=i+k;temp++)map[A][eid[1][temp][j]]=map[A][eid[1][temp][j+k+1]]=1;
}
for(i=1;i<=m;i++)
{
scanf("%d",&k);
for(j=1;j<=nmrx;j++)if(map[j][k])destroy[j]=1;
}
for(i=1;i<=nmrx;i++)if(!destroy[i])
{
U[i]=D[i]=i;
L[i]=L[0],R[i]=0;
L[0]=R[L[0]]=i;
}
cnt=nmrx;
for(i=1;i<=nmrx;i++)
if(!destroy[i])
for(j=1;j<=npon;j++)
if(map[i][j])
newnode(j,i);
}
inline void remove(int x)
{
int i=x;
do{
R[L[i]]=R[i];
L[R[i]]=L[i];
i=D[i];
}while(i!=x);
}
inline void resume(int x)
{
int i=x;
do{
R[L[i]]=i;
L[R[i]]=i;
i=U[i];
}while(i!=x);
}
inline void dfs(int x)
{
if(x>=ans)return ;
if(!R[0])
{
ans=x;
return ;
}
int S=R[0],W=T[S],i,j;
int del[N],num;
for(i=R[S];i;i=R[i])if(T[i]<W)
{
W=T[i];
S=i;
}
remove(S);
for(i=D[S];i!=S;i=D[i])
{
del[num=1]=R[i];
for(j=R[R[i]];j!=R[i];j=R[j])del[++num]=j;
for(j=1;j<=num;j++)remove(del[j]);
dfs(x+1);
for(j=num;j;j--)resume(del[j]);
}
resume(S);
return ;
}
}dlx;
int main()
{
// freopen("test.in","r",stdin);
// freopen("my.out","w",stdout);
int g;
scanf("%d",&g);
while(g--)
{
ans=inf;
dlx.build();
dlx.dfs(0);
printf("%d\n",ans);
}
return 0;
}

博客介绍了如何使用Dancing Links (DLX) 算法解决POJ1084问题,即在火柴拼成的网格中删除最少的火柴以破坏所有正方形。将问题转化为DLX模型,每行代表火柴,每列代表正方形。通过DLX模板实现重复覆盖问题的求解,强调在重复覆盖问题中,不需要删除与行元素相关的列,因为不需要避免重复。文章提供了关键代码片段帮助读者快速理解和解决此问题。
330

被折叠的 条评论
为什么被折叠?



