深度优先遍历:POJ2362木棍拼正方形

题目大意:
给定一组不同长度的棍子,是否可以将它们首尾相连形成一个正方形?

输入
输入的第一行包含 N,即测试用例的数量。每个测试用例都以整数 4 <= M <= 20 开始,即棒的数量。接下来是 M 个整数;每个都给出一根棍子的长度一个介于 1 和 10,000 之间的整数。

输出
对于每种情况,如果可以形成正方形,则输出包含“yes”的行;否则输出“no”。

样本输入
3
4 1 1 1 1
5 10 20 30 40 50
8 1 7 2 6 4 4 3 5

样本输出
yes
no
yes

【分析】
题面要求判断能否搜索出一个方案让这些木棍形成正方形。首先,将木棍长度进行累加,得到总长length;然后将length除以4得到正方形的边长side;之后,将木棍拼凑成长度为side 的边,拼凑完一条边后,再拼凑下一条,直到拼凑出4条长度为side 的边,从而构成一个正方形。
搜索状态三元组(sum, number ,position),其中 sum是当前拼凑的木棍长度,number是已拼凑成边长的数量, position是当前木棍的编号。需要搜索到的目标状态为number=3。为什么不是4呢﹖因为如果总长length能够整除4,且已经拼凑出3条边,那么剩下的木棍必定可以构成最后的一条边。由于从第一根木棍开始拼凑,故搜索初始状态为(0,0,0)。
在搜索过程中,可以通过放弃对某些不可能产生结果的子集的搜索,达到提高效率的目的。这样的技术称为剪枝。本题中可以剪枝的途径如下:
①如果总长length不能整除4,那么必定无法构成正方形。
②如果某根木棍的长度大于边长side,那么必定无法构成正方形。
③拼凑中发现某长度的木棍无法构成当前边时,再遇等长木棍时可跳过(需排序)。

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;

const int Max = 25;
bool visit[Max];
int a[Max];
int side, m;

bool DFS(int sum, int num, int position){
	if(num == 3)
		return true;
	int sample = 0;		//剪枝3:记录上一次失败的长度
	for(int i = position; i < m; i++){
		if(a[i] > side || visit[i] == true || sum + a[i] > side || a[i] == sample)
			continue;
		visit[i] = true;
		//cout << "sum + a["<<i<<"]="<<sum + a[i]<<"  side=" << side << "  num="<<num<<endl;
		if(sum + a[i] == side){
			if(DFS(0, num + 1, 0))
				return true;
			else
				sample = a[i];
		}else{
			if(DFS(sum + a[i], num, i + 1))
				return true;
			else
				sample = a[i];
		}
		visit[i] = false;
	}
	return false;
}

bool cmp(int x, int y){
	return x > y;
}

int main(){
	int n;
	cin >> n;
	while(n--){
		int length = 0;
		cin >> m;
		for(int i = 0; i < m; i++){
			cin >> a[i];
			length += a[i];
		}
		memset(visit, false, sizeof(visit));
		if(length % 4 != 0)		//剪枝1
			cout << "no" <<endl;
		else{
			side = length / 4;
			sort(a, a + m, cmp);
			if(a[0] > side)		//剪枝2
				cout <<  "no" << endl;
			visit[0] = true;
			if(DFS(0, 0, 0))
				cout <<  "yes" << endl;
			else
				cout <<  "no" << endl;
		}
	}
	system("pause");
	return 0;
}
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
A:木棍还原问题是一种常见的深度优先算法问题。具体的思路是,先将破碎的木棍抽象成一个图形,然后从任意一根木棍开始,以逆时针的顺序将其端点遍历,并将遍历过的木棍断开,变成两根更短的木棍。然后从任意一条与当前木棍相邻的木棍再次开始遍历,直到所有的木棍都被接完成或者无法接。 具体步骤如下: 1. 选择一根木棍,从其中任意一端开始遍历它的邻接木棍。 2. 对于每个邻接木棍,重复1,直到所有邻接木棍被遍历过。 3. 如果当前木棍的两端都没有邻接木棍,则将其标记为已经访问,从当前栈中弹出并返回上一级调用。 4. 否则,选择一个未曾遍历的邻接木棍,将其作为当前木棍,将其标记为已访问并进入栈中,从1开始。 在实现深度优先算法时,需要注意以下几点: 1. 应该使用栈来维护当前遍历路径。 2. 应该记录每个木棍是否已经访问过,避免重复访问。 3. 应该检测当前木棍的邻接木棍是否能够接,如果不能,应该回溯到上一个节点尝试其他路径。 代码示例: ``` def dfs(stick, path): if len(path) == len(stick): # 所有木棍已经还原完成 return True for i in range(len(stick)): if not visited[i]: # 当前木棍未被访问 visited[i] = True path.append(stick[i]) for j in range(i+1, len(stick)): if not visited[j] and sum(path) + stick[i] + stick[j] <= total_length: visited[j] = True path.append(stick[j]) if sum(path) == total_length: # 可以完成接 return True if dfs(stick, path): # 搜索下一级节点 return True path.pop() visited[j] = False path.pop() visited[i] = False return False ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值