深度优先遍历: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
#include
#include
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["<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;
}

你可能感兴趣的:(算法C/C++,深度优先,算法,c++)