【POJ1084】Square Destroyer 重复覆盖问题 DLX(Dancing Links)

    首先给一个传送门,是我写的精确覆盖问题,也是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;
}


你可能感兴趣的:(题解,Links,dlx,Dancing,POJ1084,重复覆盖问题)