蓝桥——Sticks(dfs+剪枝,POJ 1011)

试题 算法训练 Sticks

资源限制

时间限制:1.0s   内存限制:999.4MB

  Sticks

Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 113547 Accepted: 26078

问题描述

  George took sticks of the same length and cut them randomly until all parts became at most 50 units long. Now he wants to return sticks to the original state, but he forgot how many sticks he had originally and how long they were originally. Please help him and design a program which computes the smallest possible original length of those sticks. All lengths expressed in units are integers greater than zero.

输入格式

  The input contains blocks of 2 lines. The first line contains the number of sticks parts after cutting, there are at most 64 sticks. The second line contains the lengths of those parts separated by the space. The last line of the file contains zero.

输出格式

  The output should contains the smallest possible length of original sticks, one per line.

样例输入

9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0

样例输出

6
5

题目大意:这个人把一堆等长的木棍切割成n块小木棍,后来他想把他们拼成同样长度的木棍,但是他不知道原始的木棍长度是多少,也不知道原始木棍的数目,所以现在就问要把这一堆木棍拼成同等长度的大木棍,需要拼成木棍的最短的长度。

思路:用dfs把满足的长度搜一遍,注意剪枝

剪枝:

1. 木棍符合结果的长度一定是总木棍长度的因数

2. 将木棍从大到小排序,则可以在比较少的次数里合成想要的那种长度 

3. 在搜索过程中,如果前一个木棍没有成功拼接,那么后面与其相同的木棍也要被剪掉

4. 当一个木棍拼凑失败时,直接回溯,不再按照这个条件浪费时间

 

代码:

#include <bits/stdc++.h>
#define Max 101
using namespace std;
int sticks[Max],vis[Max];
int min_len; // 满足条件的最小长度
int stick_num;//  木棍数量 
int n;// stick的数量
int total_len; // 木棍总长度
int cmp ( int a, int b){
	return a > b;
} 
/*
	sum 指当前拼凑的这根木棍的长度
	cur 指当前正在搜索的木棍下标 
	res 表示已经拼成的木棍的数量
	k 表示假设的单个木棍的长度   ->  min_len 
*/
bool dfs(int sum, int cur, int res, int k){
	if(res == stick_num){
		return true;
	}
	for(int i = cur; i < n; i++){
		//第i个被用了,或者与前一个木棍长度相同但是 前一个也没被用 
		//那么 这个也不能被用 
		if(vis[i] || (i && sticks[i] == sticks[i-1] && !vis[i-1])){
			continue;
		}
		if(sticks[i] + sum == k) {
			vis[i] = 1;
			if(dfs(0, 0, res+1, k)){
				return true;
			}
			vis[i] = 0; //虽然这步拼成了长度为x的木棍,但是剩下的不能成功拼凑,所以失败回溯 
			return false;
		}
		if(sticks[i] + sum < k) { //没拼好 
			vis[i] = 1;
			if(dfs(sticks[i] + sum, i + 1, res, k)){
				return true;
			}
			vis[i] = 0;
			if(!sum) return false;
		}
	}
	return false;
} 
int main(){
	while(cin >> n && n) {
		total_len = 0;
		for(int i = 0; i < n; i++) {
			cin >> sticks[i];
			total_len += sticks[i];
		}
		//从大到小排序  可以在比较少的次数里合成想要的那种长度 
		sort(sticks, sticks + n, cmp);
		int flag = 0;
		for(min_len = sticks[0]; min_len < total_len; min_len++){
			//因为初始的木棍是等长的 
			if(total_len % min_len == 0) {
				memset(vis, 0, sizeof(vis));
				stick_num = total_len / min_len; // 可能的最多木棍数量 
				if(dfs(0,0,0,min_len)){
					cout << min_len << endl;
					flag =  1;
					break;
				}
			}
		} 
		if(!flag) {
			cout << total_len << endl;
		}
	}
	return 0;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值