POJ 1011 Sticks 解题报告

参考(其实是完全移植了)这里的解题报告:http://blog.sina.com.cn/s/blog_6635898a0100lgq0.html。

感觉搜索题做伤了。短期之内不会再做了。

首先,sticks要从大到小排序。因为大的stick相对难找,一开始就确定好有利于更快地确定解。

其次,记下当前正在匹配的stick,下次从这个stick之后开始试。

再次,如果当前的stick和前一个stick的长度一样,而前一个stick没有选(前一个stick肯定一个尝试过了,没选是因为没有解),说明这个stick也不用尝试了。

个人认为要点在于链接中的第3点,即如果当前len等于0,即(匹配完了上一段或最开始),新开始一段时,只需要取第一个没选的(也是最大的,因为sticks已经从大到小排序好了。)。不用再像普通循环里面的那样,一个一个尝试了。因为如果有解,这个第一个stick迟早要选,而且位置在哪里都不关心,所以第一个选它就可以了。

code已经改得和链接上面几乎完全一样了。这里为了日后记录,遂粘贴与此。

#include <algorithm>
#include <iostream>
using namespace std;

const int MAXN = 65;
int N, len;
int sticks[MAXN];
bool mark[MAXN];
bool flag;

void recur(int depth, int l, int k)
{
	if(flag)
	{
		return;
	}
	
	if(l == 0)
	{
		int i = 0;
		while(mark[i])
		{
			i++;
		}
		mark[i] = true;
		recur(depth + 1, sticks[i], i + 1);
		mark[i] = false;
		return;
	}
	

	if(l == len)
	{
		if(depth == N)
		{
			flag = true;
		}
		else
		{	
			recur(depth, 0, 0);
		}
		return;
	}

	for(int i = k; i < N; i++)
	{
		if(!mark[i] && l + sticks[i] <= len)
		{
			if(!mark[i - 1] && sticks[i - 1] == sticks[i])
			{
				continue;
			}

			mark[i] = true;

			recur(depth + 1, l + sticks[i], i + 1);
			
			mark[i] = false;
		}
	}
}

int main()
{
	while(scanf("%d", &N) && N != 0)
	{
		flag = false;
		int sum = 0;
		for(int i = 0; i < N; ++i)
		{
			fscanf(stdin, "%d", &sticks[i]);
			sum += sticks[i];
			// fprintf(stdout, "sticks[%d]: %d\n", i, sticks[i]);
		}

		sort(sticks, sticks + N, greater<int>());

		for(len = sticks[0]; len <= sum; ++len)
		{
			if(sum % len == 0)
			{
				// memset(mark, false, N * sizeof(bool));
				memset(mark, false, sizeof(mark));
				recur(0, 0, 0);
				if(flag)
				{
					fprintf(stdout, "%d\n", len);
					break;
				}
			}
		}
	}
	return 0;
}


你可能感兴趣的:(POJ 1011 Sticks 解题报告)