POJ1011

少林神棍

DFS 一个非常经典的题目。北大POJ训练题
有着非常神奇的剪枝.
题目意思:给n个小木棒 要求把N个小木棒拼凑成长度 一样的木棍。
利用DFS 进行每个长度的枚举。
剪枝三:拆解拼凑第i个木棍的第一个木棒这是没有必要的因为拆掉第一个木棒说明当前剩余的木棒已经使得第i个木棍已经是拼凑不成枚举的长度
剪枝四:拆解第i个木棍的最后一个木棒k用若干个更小的木棒来替换。
这样是不合理的!
假设当前枚举的长度是成立的结果,那么k木棒一定会出现在后面拼凑木棍之中。那么用k木棒去替换i木棍最后一个位置也是成立的,说明这个替换策略是正确!
剪枝五 把每一个木棍按从大到小去拼,这样可以减少重复的计算。

#define maxn 10000

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <climits>
#include <vector>
#include <cstring>
#include <fstream>
#include<functional>
using namespace std;

vector<int> tick;
bool vis[64];//i木棒是否被使用
int n,T,St;
bool dfs(int m, int L)
{
	if (m == 0 && L == 0)
	{
		return true;
	}
	if (L == 0)
	{
		L = T;
	}
	int SN = 0;
	if (L != T)
	{
		SN = St + 1;//剪枝5
	}
	for (int i = SN; i < n; i++)
	{
		if (!vis[i]&&tick[i]<=L)
		{
			if (i > 0)
			{
				if (vis[i - 1] == false && tick[i - 1] == tick[i])
				{
					continue; //剪枝 2 如果在拼凑x个木棍的时候用了第i木棒并且拼凑失败了没有必要再枚举第i个木棒
				}
			}
			vis[i] = true;//i木棒用过了
			St = i;//记录当前枚举到了i个木棍的位置
			if (dfs(m - 1, L - tick[i]))
			{
				return true;
			}
			else
			{
				vis[i] = false;
				if (L==tick[i]||L == T) //剪枝3,4 如果L拆了第一个木棒和最后一个木棍的话那么搜索变的没有必要性
				{
					return false;
				}
			}
		}
	}
	return false;
}


int main()
{
	while (~scanf("%d",&n)&&n)
	{
		int sum = 0;
		tick.clear();
		for (int i = 0; i < n; i++)
		{
			int s;
			scanf("%d", &s);
			tick.push_back(s);
			sum += s;
		}
		sort(tick.begin(), tick.end(),greater<int>());//木棍从大到小排序
		int i;
		for (i = tick[0]; i <=sum/2 ; i++)
		{
			if (sum%i)//如果枚举的长度不足以被sum整除那是不可能拼成长度一样的木棍
			{
				continue;
			}
			memset(vis, false, sizeof(vis));
			T = i;
			if (dfs(n, i))
			{
				printf("%d\n", i);
				break;
			}
		}
		if (i > sum / 2)
		{
			printf("%d\n", sum);
		}
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值