POJ 3740 Easy Finding(舞蹈链)

/*
舞蹈链模板题
*/

#include <cstdio>
#include <cstring>
const int mMax = 50, nMax = 500;
int map[mMax][nMax];
int M, N;
struct Node
{
	int left, right, up, down;
	int col;
}node[mMax * nMax];//双向链表
int len;
int cnt[nMax];

void init(int n)
{
	int i;
	for(i = 0; i <= n; ++ i)
	{
		node[i].left = i - 1;
		node[i].right = i + 1;
		node[i].col = i;//
		node[i].down = i;
		node[i].up = i;
	}
	node[n].right = 0;
	node[0].left = n;
}

void remove(int c)
{
	int i, j;
	node[node[c].left].right = node[c].right;
	node[node[c].right].left = node[c].left;
	for(i = node[c].down; i != c; i = node[i].down)
	{
		for(j = node[i].right; j != i; j = node[j].right)
			//因为每列只能包含一个1,所以可以把所有的列中对应的行全部删掉。
			//这里还有一个细节,因为前面执行过删除列操作,所以不需要对i再执行删除行操作,因为以后肯定不会再访问到
		{
			node[node[j].up].down = node[j].down;
			node[node[j].down].up = node[j].up;
			cnt[node[j].col] --;
		}
	}
}

void resume(int c)//先删除的后恢复
{
	int i, j;
	for(i = node[c].up; i != c; i = node[i].up)
	{
		for(j = node[i].left; j != i; j = node[j].left)
		{
			node[node[j].up].down = j;
			node[node[j].down].up = j;
			cnt[node[j].col] ++;
		}
	}
	node[node[c].left].right = c;
	node[node[c].right].left = c;

	//先删除先恢复,也正确,但是有些情况会出现偏差,无法与原来状态对称
	/*int i, j;
	node[node[c].left].right = c;
	node[node[c].right].left = c;
	for(i = node[c].down; i != c; i = node[i].down)
	{
		for(j = node[i].right; j != i; j = node[j].right)
		{
			node[node[j].up].down = j;
			node[node[j].down].up = j;
			cnt[node[j].col] ++;
		}
	}*/
}

bool dfs(int k)//k表示已经找出了多少行
{
	int i, j;
	if(node[N].right == N)//终止状态:列已经被全部删除。这里需要一个空节点,来进行判断
		return true;
	int c, _min = 0x7fffffff;
	for(i = node[N].right; i != N; i = node[i].right)
	{
		if(cnt[i] < _min)
		{
			_min = cnt[i];
			c = i;
		}
	}
	remove(c);
	for(i = node[c].down; i != c; i = node[i].down)
	{
		//找到"一行"可能的解,然后递归
		for(j = node[i].right; j != i; j = node[j].right)
		{
			remove(node[j].col);
		}
		if(dfs(k + 1)) return true;
		for(j = node[i].right; j != i; j = node[j].right)
		{
			resume(node[j].col);
		}
	}
	resume(c);
	return false;
}

int main()
{
	//freopen("e://data.in", "r", stdin);
	while(scanf("%d%d", &M, &N) != EOF)
	{
		int i, j;
		init(N);
		int cur;
		memset(cnt, 0, sizeof(cnt));
		cur = N + 1;
		for(i = 0; i < M; ++ i)
		{
			int start = cur;
			node[start].left = start;//初始化,为了形成环
			node[start].right = start;
			for(j = 0; j < N; ++ j)
			{
				scanf("%d", &map[i][j]);
				if(map[i][j] == 1)
				{
					node[cur].right = node[start].right;//这里需要四步操作,原来漏泄了③,一直WA。模拟双向链表的头部插入操作
					node[cur].left = start;
					node[node[start].right].left = cur;//③
					node[start].right = cur;

					node[cur].down = node[j].down;
					node[cur].up = j;
					node[node[j].down].up = cur;
					node[j].down = cur;

					node[cur].col = j;
					cur ++;
					cnt[j] ++;
				}
			}
		}			
		if(dfs(0))
			printf("Yes, I found it\n");
		else
			printf("It is impossible\n");
	}
	return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值