POJ1011 选木棒 DFS+剪枝

本文介绍了一种使用深度优先搜索(DFS)算法解决棒子切割问题的方法。通过枚举可能的切割长度并利用DFS进行状态转移,最终找到切割棒子的最大长度。文章详细解释了算法思想,并提供了完整的代码实现。

概述:

继续练习DFS的算法,便百度到了这一题,相比之前的两道题,这个题目却是可以称为一道难题了。

我一直觉得ACM中最难的就是把问题抽象为数学模型的步骤,以及联系过往知识算法的能力,这也是我最想要锻炼的东西。

本人不才,想了没多久发现不会做就去搜索别人的代码了。想要在初级阶段先通过别人的代码让自己熟悉起来,这样或许可以慢慢得心应手。


题目描述:

给定n个棒子,知道他们都是由x根长的棒子切割而来。求x的长度的最小值。(就是最大的x数目嘛)


算法思想:

最重要的思想仍然是DFS。

首先记录下每一根棒子的长度,用数组。然后记下来总长度是多少。因为最后的x长度一定能整除总长度。所以就枚举这些个长度。

这时候注意,是对于每一个固定的长度进行枚举。

在枚举的过程中,record数组的作用是记录当前的这根木棒是不是已经使用过了(1)还是没使用过(0),有这个数组的原因是因为要不断地调整子状态,去枚举来看是不是最后能够达到正好组合成功的状态。

这之后的dfs函数分为三个部分。

对于每一次长度的枚举,入口都是在cur_len = 0那个case下,在那个case下开启一轮dfs,然后结束之后把该点设回去,回到主函数,如果没有success就重新来一遍。

对于过程中,这里直接读代码吧我觉得可读性并不是很差。重要的是要理解整个的思想,是如何跳转状态,如果不会的话可以拿笔写下来。我个人也是琢磨了很长时间才能想明白这个方法。

最后就是cin和cout实在太慢啦,以后竞赛的时候我要避免这两者的使用。

代码部分:

#include <iostream>
#include <algorithm>
#include <string.h>
#include <stdio.h>
using namespace std;
int n,stick[67];
int len;
bool success,record[67];

bool cmp(int a, int b) { 
	return a > b;
}

void dfs(int dep, int cur_len, int u) {
	if (success) return;
	if (cur_len == 0) {
		int k = 0;
		while (record[k]) k++;
		record[k] = 1;
		dfs(dep + 1, stick[k], k + 1);
		record[k] = 0;
		return;
	}

	if (cur_len == len) {
		if (dep == n) success = true;
		else dfs(dep, 0, 0);
		return;
	}

	for (int i = u; i < n; ++i) {
		if (!record[i] && cur_len + stick[i] <= len) {
			if (!record[i - 1] && stick[i] == stick[i - 1]) continue;
			record[i] = 1;
			dfs(dep+1, cur_len + stick[i], i + 1);
			record[i] = 0;
		}
	}
}

int main() {
	while (scanf("%d",&n) && n != 0) {
		int sum = 0;
		success = false;
		for (int i = 0; i < n; ++i) {
			scanf("%d",&stick[i]);
			sum += stick[i];
		}
		sort(stick, stick + n, cmp);
		for (len = stick[0]; len < sum; ++len) {
			if (sum % len == 0) {
				memset(record, 0, sizeof(record));
				dfs(0, 0, 0);
				if (success) break;
			}
		}
		printf("%d\n", len);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值