POJ 1011 Sticks

题目大意:

        乔治将几根长度一样的木棍随机砍断,得到若干长度随机的小木棍,现在他想把这些小木棍拼回去,但是已经忘记掉原木棍的长度和根数,现在请你编程确定原木棍最短为多少。

        现有多个测例,每个测例都给出随机长度小木棍的数量n(n不超过64),并给出每根小木棍的长度(小木棍超度不超过50),要求输出可能的原木棍的最小长度,以n = 0作为输入的结束。

题目链接

注释代码:

/*           
 * Problem ID : POJ 1011 Sticks
 * Author     : Lirx.t.Una           
 * Language   : GCC          
 * Run Time   : 0 ms           
 * Run Memory : 360 KB           
*/ 

#pragma GCC optimize("O2")

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#define	TRUE		1
#define	FALSE		0

//maximum number of sticks
//木棍的最大数量
#define	MAXSTKN		64

typedef	char	BOOL;

short	len[MAXSTKN];//每条木棍的长度,从下标0计
BOOL	usd[MAXSTKN];//used,表示第i号棍子是否使用过

int
fcmp(const void *a, const void *b) {
	
	return *(short *)b - *(short *)a;
}

//思路:由于每一个目标段中都至少包含一条小木棍
//因此目标段的最小长度至少为小木棍的最大长度
//因此可以先将小木棍按照长度从大到小排序
//然后在贪心选择的基础上DFS

BOOL
dfs( int ncplt, short cl, int cur, int ns, short sl, int n ) {
	//completed number,已经拼好的棍子的数量
	//current length,目前已经拼凑了多长(是指一个段里)
	//current stick,当前正扫描的木棍的编号
	//segment number,目标段的个数
	//segment length,目标段的长度
	//totol stick number,小木棍的总数量
	
	if ( ncplt == ns )//如果已经拼完所有目标段则成功退出
		return TRUE;
	
	for ( ; cur < n; cur++ ) {//否则就从当前木棍开始检查
		
		//!!!剪枝1
		//如果当前木棍已被使用过了
		//或者是和已经拼好的长度相加超过目标段长
		//则直接跳过
		if ( usd[cur] || cl + len[cur] > sl )
			continue;

		//否则就可以标记为使用过		
		usd[cur] = TRUE;
		//否则就可以标记为使用过,接下来做相应的检查
		
		//否则就可以标记为使用过,接下来做相应的检查
		if ( cl + len[cur] == sl ) {
			
			//则继续搜索
			//此时因为cl和len[cur]已经拼好了一个完整的段
			//因此进入下一层搜索时拼好的数量就是ncplt+1了
			//并且拼好的长度清零
			//并且从头开始扫描
			if ( dfs( ncplt + 1, 0, 0, ns, sl, n ) )
				return TRUE;
			
			//!!!剪枝2
			//如果剩下的所有小木棍无法完成目标
			//则表示当前方案失败
			//因为若不使用len[cur],但是为了完成目标
			  //接下来必定要找到能和cl组成完整段的小木棍
			  //即使能找到,那这些和cl组成完整段的小木棍的效果
			  //和len[cur]是一样的,所以就算这一层中不适用len[cur]
			  //后面的搜索同样是不成功的
			//因此这里需要剪枝,不能往下搜索了
			return usd[cur] = FALSE;
		}
		
		//接下来就是cl + len[cur] < sl的情况了

		//继续下一层搜索,只不过拼好的长度为cl + len[cur]了
		//由于当前还没有拼完一段目标段,因此还是ncplt
		//并且得从cur + 1的位置继续扫描
		//因为len是从大到小排序过的(方便贪心选择)
		if ( dfs( ncplt, cl + len[cur], cur + 1, ns, sl, n ) )
			return TRUE;
		
		//如果不成功,则有可能替换len[cur]
		usd[cur] = FALSE;
		//!!!剪枝3
		//如果cl = 0,则表示拿len[cur]和其它剩下的木棍凑,凑不出目标
		//因此只能失败退出
		if ( !cl )
			return FALSE;
		
		//!!!剪枝4
		//否则就表示cl和len[cur]组合在一起是不能和剩下的小木棍凑出目标
		//这就意味着cl和其它len组合可能凑出目标
		//因此尝试换其它len和cl组合
		//但是得避免后面重复测试和len[cur]一样长的木棍
		while ( cur + 1 < n && len[cur] == len[cur + 1] )
			cur++;
	}
	
	return FALSE;//所有小木棍都检测完仍然凑不出
}

int
main() {
	
	int		n;//木棍数量
	
	short	tl;//totol length,小木棍总长
	short	ns;//number of segment,目标段数量
	short	sl;//length of segment,目标段长度(sl = tl / ns) 
	
	BOOL	cd;//can be done,用于标志当前sl能否被成功凑出
	
	int		i;
	
	while ( scanf("%d", &n), n ) {
		
		for ( tl = 0, i = 0; i < n; i++ ) {
			
			scanf("%d", len + i);
			tl += len[i];
		}
		qsort(len, n, sizeof(short), &fcmp);
		
		cd  = FALSE;//初始化
		//!!!剪枝5
		//不用sl从len[0]一直++到tl的方式进行搜索
		//因为最多只能被分为tl / len[0]段,最少分为1段
		//用段数来扫描比直接用段长++扫描的方式少很多判( tl % sl ) == 0的环节
		for ( ns = tl / *len; ns >= 2; ns-- )//!!!剪枝6,如果ns = 2也不行,则ns = 1是必然成立的
			                                 //因此不用麻烦地对ns = 1也进行搜索了!!!
			if ( !( tl % ns ) ) {
				
				memset(usd, FALSE, sizeof(usd));
				if ( dfs( 0, 0, 0, ns, sl = tl / ns, n ) ) {
					
					cd = TRUE;
					break;
				}
			}
			
		if ( cd )
			printf("%d\n", sl);
		else
			printf("%d\n", tl);
	}
	
	return 0;
}

无注释代码:

#pragma GCC optimize("O2")

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#define	TRUE		1
#define	FALSE		0

#define	MAXSTKN		64

typedef	char	BOOL;

short	len[MAXSTKN];
BOOL	usd[MAXSTKN];

int
fcmp(const void *a, const void *b) {

	return *(short *)b - *(short *)a;
}

BOOL
dfs( int ncplt, short cl, int cur, int ns, short sl, int n ) {

	if ( ncplt == ns )
		return TRUE;

	for ( ; cur < n; cur++ ) {
	
		if ( usd[cur] || cl + len[cur] > sl )
			continue;

		usd[cur] = TRUE;

		if ( cl + len[cur] == sl ) {
		
			if ( dfs( ncplt + 1, 0, 0, ns, sl, n ) )
				return TRUE;
			
			return usd[cur] = FALSE;
		}

		if ( dfs( ncplt, cl + len[cur], cur + 1, ns, sl, n ) )
			return TRUE;

		usd[cur] = FALSE;
		if ( !cl )
			return FALSE;

		while ( cur + 1 < n && len[cur] == len[cur + 1] )
			cur++;
	}
	
	return FALSE;
}

int
main() {
	
	int		n;
	
	short	tl;
	short	ns;
	short	sl;
	
	BOOL	cd;
	
	int		i;
	
	while ( scanf("%d", &n), n ) {
		
		for ( tl = 0, i = 0; i < n; i++ ) {
			
			scanf("%d", len + i);
			tl += len[i];
		}
		qsort(len, n, sizeof(short), &fcmp);
		
		cd  = FALSE;
		for ( ns = tl / *len; ns >= 2; ns-- )
			if ( !( tl % ns ) ) {
				
				memset(usd, FALSE, sizeof(usd));
				if ( dfs( 0, 0, 0, ns, sl = tl / ns, n ) ) {
					
					cd = TRUE;
					break;
				}
			}
			
		if ( cd )
			printf("%d\n", sl);
		else
			printf("%d\n", tl);
	}
	
	return 0;
}

优化:

注释代码:

/*           
 * Problem ID : POJ 1011 Sticks
 * Author     : Lirx.t.Una           
 * Language   : C++          
 * Run Time   : 0 ms           
 * Run Memory : 136 KB           
*/ 

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>

#define	MAXN	64

using namespace std;

char	len[MAXN];
bool	usd[MAXN];

int		n;//小棍个数
int		len_seg;//length of each segment,目标段长度

bool
dfs( int n_rst, int seg_len_rst, int cur_i ) {
	//number of rest sticks,剩下没有拼完的小棍数
	//rest segment length,目标段还有多少没被拼完
	//current ith sticks,当前检测到第cur_i号小棍(下标从0开始)
	
	if ( !n_rst && !seg_len_rst ) return true;//全部拼完
	
	if ( !seg_len_rst ) {//小棍没用完,但是当前目标段为0
		//表示刚拼完一个目标段,还需用剩下的小棍拼目标端
		
		seg_len_rst = len_seg;//初始化当前目标段待拼的剩余长度
		cur_i = 0;//按照目标端从左到右小棍长度降低的规则从头开始检测小棍
	}
	
	for ( ; cur_i < n; cur_i++ )
		if ( !usd[cur_i] && len[cur_i] <= seg_len_rst ) {//检测的小棍不能大于当前剩余目标段长
			
			usd[cur_i] = true;//符合要求,先试探性使用一下该小棍
			                                               
			                                               //目标段中小棍从左到右长度递减
			                                               //因此一下次搜索cur_i必须前进一格
			if ( dfs( n_rst - 1, seg_len_rst - len[cur_i], cur_i + 1 ) ) return true;
			usd[cur_i] = false;//搜索失败,退还该木棍       //剪枝1
			
			                      
			     //如果当前小木棍是当前    //如果当前小木棍是当前
			     //目标段的头              //目标段的尾
			//则表示是上一次检测的小木棍有问题,不用继续替换当前小木棍,因此直接失败退出
			if ( seg_len_rst == len_seg || seg_len_rst == len[cur_i] ) return false;
                         //剪枝2                   //剪枝3

			                         //虽然当前小木棍既不是当前目标段的头也不是尾
			                         //但反正是失败了,因此在接下来的搜索中排除和它一样长的小木棍
			while ( cur_i + 1 < n && len[cur_i] == len[cur_i + 1] ) cur_i++;
		}                                    //剪枝4
		
	return false;//一直都没搜出答案,失败退出
}

bool
fcmp( char a, char b ) {
	
	return a > b;
}

int
main() {
	
	int		len_tot;//所有小棍总长度
	int		n_seg;//假设的目标段的个数
	
	int		i;
	
	bool	done;
	
	while ( scanf("%d", &n), n ) {
		
		len_tot = 0;
		for ( i = 0; i < n; i++ ) {
			
			scanf("%d", len + i);
			len_tot += len[i];
		}
		sort(len, len + n, fcmp);
		
		done = false;
		for ( n_seg = len_tot / *len; n_seg > 1; n_seg-- )
			if ( !( len_tot % n_seg ) ) {
				
				memset(usd, 0, sizeof(usd));
				if ( dfs( n, len_seg = len_tot / n_seg, 0 ) ) {
					
					done = true;
					break;
				}
			}
			
		if ( done ) printf("%d\n", len_seg);
		else printf("%d\n", len_tot);
	}
	
	return 0;
}
无注释代码:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>

#define	MAXN	64

using namespace std;

char	len[MAXN];
bool	usd[MAXN];

int		n;
int		len_seg;

bool
dfs( int n_rst, int seg_len_rst, int cur_i ) {

	if ( !n_rst && !seg_len_rst ) return true;

	if ( !seg_len_rst ) {
		
		seg_len_rst = len_seg;
		cur_i = 0;
	}

	for ( ; cur_i < n; cur_i++ )
		if ( !usd[cur_i] && len[cur_i] <= seg_len_rst ) {
		
			usd[cur_i] = true;
			if ( dfs( n_rst - 1, seg_len_rst - len[cur_i], cur_i + 1 ) ) return true;
			usd[cur_i] = false;

			if ( seg_len_rst == len_seg || seg_len_rst == len[cur_i] ) return false;
			while ( cur_i + 1 < n && len[cur_i] == len[cur_i + 1] ) cur_i++;
		}

	return false;
}

bool
fcmp( char a, char b ) {

	return a > b;
}

int
main() {

	int		len_tot;
	int		n_seg;

	int		i;

	bool	done;

	while ( scanf("%d", &n), n ) {
	
		len_tot = 0;
		for ( i = 0; i < n; i++ ) {
		
			scanf("%d", len + i);
			len_tot += len[i];
		}
		sort(len, len + n, fcmp);

		done = false;
		for ( n_seg = len_tot / *len; n_seg > 1; n_seg-- )
			if ( !( len_tot % n_seg ) ) {
			
				memset(usd, 0, sizeof(usd));
				if ( dfs( n, len_seg = len_tot / n_seg, 0 ) ) {
				
					done = true;
					break;
				}
			}

		if ( done ) printf("%d\n", len_seg);
		else printf("%d\n", len_tot);
	}

	return 0;
}
单词解释:

randomly:adv, 随机地,任意地

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值