您的位置:首页 > 其它

POJ 1011 Sticks

2014-04-22 21:41 525 查看
题目大意:

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

现有多个测例,每个测例都给出随机长度小木棍的数量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, 随机地,任意地
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: