zoj 3209 Treasure Map 最小覆盖问题 dancing links

这是练手的一道dancing links题,直接套用上篇博文那题的代码即可,不过得把问题稍微转化以下。

地图是个m行n列的地图,即有n*m个小块,共有p个图片去覆盖,那么我们只要找出每个小块和p个图片的关系,构造一个p行n*m列的矩阵,那么第j行第j列表示,j对应的那个小块能被图片i覆盖

剩下的问题,就是如何选出最少的行数使得每列被覆盖即可(可以参见上一篇文章 http://blog.csdn.net/cen5bin/article/details/9713089


附上代码:

#include <cstdio>
#include <cstring>
const int M = 1010;
const int N = M*M;
int R[N],L[N],U[N],D[N],C[N];
int S[M];
int n,m;
int ans; // 需要拿掉几行
int size; //节点个数

void init()
{
	memset(S,0,sizeof(S)); //初始的时候每列上1的个数为0

	//链表初始化
	for(int i=1; i<=m; i++)
	L[i+1] = R[i-1] = U[i] = D[i] = i;
	R[m] = 0; //0表示表头
	size = m + 1; //新的节点的下标是从m+1开始的,当然也可以随机的设置一个更大的值,没有影响
}

//删除第c列,以及该列中元素对应的所有行
void remove(int c)
{
	R[L[c]] = R[c];
	L[R[c]] = L[c];
	for(int i=D[c]; i!=c; i=D[i])
	for(int j=R[i]; j!=i; j=R[j])
	{
		U[D[j]] = U[j];
		D[U[j]] = D[j];
		S[C[j]]--;
	}
}

//恢复第c列,以及该列中元素对应的所有行
void resume(int c)
{
	for(int i=U[c]; i!=c; i=U[i])
	for(int j=L[i]; j!=i; j=L[j])
	{
		U[D[j]] = D[U[j]] = j;
		S[C[j]]++;
	}
	R[L[c]] = L[R[c]] = c;
}

void dfs(int k) //k表示当前已经拿掉的行数
{
	if(R[0]==0) //表示所有的列已经被拿完
	{
		if(ans==-1||ans>k) ans = k;
		return;
	}
	int min = M,c=-1;
	//选取元素个数最少的列
	for(int i=R[0]; i!=0; i=R[i])
	if(S[i]<min) min = S[i],c = i;
	remove(c);
	for(int i=D[c]; i!=c; i=D[i])
	{
		for(int j=R[i]; j!=i; j=R[j])			remove(C[j]);
		dfs(k+1);
		for(int j=L[i]; j!=i; j=L[j])
		resume(C[j]);
	}
	resume(c);
	
}

void insertCol(int col)
{
	C[size] = col;
	U[size] = U[col];
	D[U[col]] = size;
	U[col] = size;
	D[size] = col;
	S[col]++;
}

void insertRow(int &rowhead)
{
	if(rowhead==-1)
	{
		L[size] = R[size] = size;
		rowhead = size;
	}
	else
	{
		L[size] = L[rowhead];
		R[L[rowhead]] = size;
		L[rowhead] = size;
		R[size] = rowhead;
	}
}

int main()
{
	//freopen("in","r",stdin);
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int a,b;
		scanf("%d%d%d",&a,&b,&n);
		m = a * b;
		init();
		for(int i=1; i<=n; i++)
		{
			int x1,x2,y1,y2;
			scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
			x1++,y1++;
			int rowhead = -1;
			for(int j=x1; j<=x2; j++)
			for(int k=y1; k<=y2; k++)
			{
				int col = j + (k-1)*a;
				insertCol(col);
				insertRow(rowhead);
				size++;
			}
		}
		ans = -1;
		dfs(0);
		printf("%d\n", ans);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值