ACM搜索题经典 Sticks

ACM搜索题经典 Sticks

/**/ /*
EOJ  1981 Sticks
POJ  1011 Sticks
HDOJ 1455 Sticks
UVA  307  Sticks


----问题描述:

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


----输入:

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


----分析:

从短到长枚举原始木棍长度,然后尝试能否拼装成功,
第一次尝试成功时的原始木棍长度,即为答案。

尝试方法,为深度优先搜索,具体剪枝策略见代码注释。


----几组比较强的测试数据:

input

64
40 40 30 35 35 26 15 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 43 42 42 41 10 4 40 40 40 40 40 40 40 40 40 40 40 40 40 40 25 39 46 40 10 4 40 40 37 18 17 16 15 40 40 40 40 40 40 40 40
64
40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 43 42 42 41 10 4 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40
64
33 36 7 45 6 14 14 14 28 39 35 36 5 33 21 32 29 37 31 3 2 19 40 34 25 1 33 49 20 14 25 36 45 37 47 28 39 8 36 44 8 48 41 1 13 18 9 10 34 42 41 39 42 20 23 6 40 28 49 16 38 33 15 7

output

454
1251
81

*/




/**/ /**********************************************************
版本三:
EOJ  1981   0MS  231K
POJ  1011  16MS  164K
HDOJ 1455   0MS  200K
UVA  307   WA

增加搜索次数限制。

(UVA 上加此限制则 WA,否则 0.212 AC,见版本二)
*/


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

#define   L  70

int  cutnum;                  //  木棍的数量
int  cutlen[ L ], totlen;     //  木棍的各自长度,及总长度

int  cmp(  const   void   * pa,  const   void   * pb)  {
        
return (*((int*)pb)) - (*((int*)pa));
}


int  orglen, orgnum;  //  原始木棍的长度及其数量
int  used[ L ];       //  dfs 时标记某木棍是否已经被用于组装原始木棍
// int choice[ L ];

//  若搜索次数超过此值,则认为不可能成功
#define   LIM  100000
int  lim;
        
//  尝试拼装某原始木棍,
        
//  此次尝试之前,
        
//  已经使用了 t 根木棍,
        
//  已经拼装出了原始木棍的部分长度,剩余 c 长度需要拼装,
        
//  此次尝试,只需要从第 b 根木棍开始。
int  dfs( int  c,  int  b,  int  t)  {
        
int i, prelen = -1;
        
if ( --lim <= 0 ) {
                
return 0;
        }

        
if ( 0 == c ) {
                
// 剩余长度为 0,则开始拼装下一根原始木棍,
                
// 需扩大尝试的范围。
                for ( i = 0; (i < cutnum) && (used[i]); ++i ) {
                }

                
// 所有木棍都已经被用于拼装原始木棍,
                
// 即尝试成功。
                if ( i >= cutnum ) {
                        
//printf("i >= cutnum, t = %d\n", t);
                        return 1;
                }

                used[ i ] 
= 1;
                
//choice[ t ] = i;
                
// 任一原始木棍中,必含有剩余木棍中最长的那根
                if ( dfs(orglen-cutlen[i], i+1, t+1) ) {
                        
return 1;
                }

                used[ i ] 
= 0;
                
return 0;
        }

        
for ( i = b; i < cutnum; ++i ) {
                
if (    (used[ i ]) ||          // 木棍没用过
                        (c < cutlen[i]) ||      // 剩余长度能容纳此木棍
                        (prelen == cutlen[i])   // 此长度的木棍已经试过,不行,无需再试
                ) {
                        
continue;
                }

                used[ i ] 
= 1;
                
//choice[ t ] = i;

                
// 下次尝试从后面的木棍开始,前面的木棍无需考虑
                if ( dfs(c-cutlen[i], i+1, t+1) ) {
                        
return 1;
                }


                prelen 
= cutlen[ i ];

                used[ i ] 
= 0;

                
// 存在一木棍刚好可以符合剩余长度,则不再尝试其它
                if ( c == cutlen[ i ] ) {
                        
return 0;
                }

        }

        
return 0;
}


int  find() {
        
// 原始木棍长度必为总长度的约数
        if ( 0 != totlen % orglen ) {
                
return 0;
        }

        orgnum 
= totlen / orglen;
        memset(used, 
0sizeof(used));
        lim 
= LIM;
        
return dfs(000);
}


int  main()  {
        
int i;
        
while ( (1 == scanf("%d"&cutnum)) && (0 < cutnum) ) {
                totlen 
= 0;
                
for ( i = 0; i < cutnum; ++i ) {
                        scanf(
"%d", cutlen+i);
                        totlen 
+= cutlen[ i ];
                }

                
// 将木棍从长到短排序,然后从长到短搜索之
                qsort(cutlen, cutnum, sizeof(cutlen[0]), cmp);

                
/**//*for ( i = 0; i < cutnum; ++i ) {
                        printf("-%d-", cutlen[ i ]);
                }
                printf("\n");
*/


                
// 原始木棍长度必大于等于最长木棍长度,
                
// 小于等于木棍总长度,
                
// 从短到长枚举原始木棍长度。
                for ( orglen = cutlen[0]; (orglen < totlen) && (! find()); ++orglen ) {
                }

                printf(
"%d\n", orglen);
                
//if ( orglen != totlen ) {
                
//        for ( i = 0; i < cutnum; ++i ) {
                
//                printf("--%d--", choice[ i ]);
                
//        }
                
//        printf("\n");
                
//}
        }

        
return 0;
}



/**/ /**********************************************************
版本二:
EOJ  1981  TLE
POJ  1011  0MS   164K
HDOJ 1455  0MS   188K
UVA  307   0.212
*/

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

#define  L  70

int cutnum;                 // 木棍的数量
int cutlen[ L ], totlen;    // 木棍的各自长度,及总长度

int cmp( const void *pa, const void *pb) {
        return (*((int*)pb)) - (*((int*)pa));
}

int orglen, orgnum; // 原始木棍的长度及其数量
int used[ L ];      // dfs 时标记某木棍是否已经被用于组装原始木棍
//int choice[ L ];

        // 尝试拼装某原始木棍,
        // 此次尝试之前,
        // 已经使用了 t 根木棍,
        // 已经拼装出了原始木棍的部分长度,剩余 c 长度需要拼装,
        // 此次尝试,只需要从第 b 根木棍开始。
int dfs(int c, int b, int t) {
        int i, prelen = -1;
        if ( 0 == c ) {
                // 剩余长度为 0,则开始拼装下一根原始木棍,
                // 需扩大尝试的范围。
                for ( i = 0; (i < cutnum) && (used[i]); ++i ) {
                }
                // 所有木棍都已经被用于拼装原始木棍,
                // 即尝试成功。
                if ( i >= cutnum ) {
                        //printf("i >= cutnum, t = %d\n", t);
                        return 1;
                }
                used[ i ] = 1;
                //choice[ t ] = i;
                // 任一原始木棍中,必含有剩余木棍中最长的那根
                if ( dfs(orglen-cutlen[i], i+1, t+1) ) {
                        return 1;
                }
                used[ i ] = 0;
                return 0;
        }
        for ( i = b; i < cutnum; ++i ) {
                if (    (used[ i ]) ||          // 木棍没用过
                        (c < cutlen[i]) ||      // 剩余长度能容纳此木棍
                        (prelen == cutlen[i])   // 此长度的木棍已经试过,不行,无需再试
                ) {
                        continue;
                }
                used[ i ] = 1;
                //choice[ t ] = i;

                // 下次尝试从后面的木棍开始,前面的木棍无需考虑
                if ( dfs(c-cutlen[i], i+1, t+1) ) {
                        return 1;
                }

                prelen = cutlen[ i ];

                used[ i ] = 0;

                // 存在一木棍刚好可以符合剩余长度,则不再尝试其它
                if ( c == cutlen[ i ] ) {
                        return 0;
                }
        }
        return 0;
}

int find(){
        // 原始木棍长度必为总长度的约数
        if ( 0 != totlen % orglen ) {
                return 0;
        }
        orgnum = totlen / orglen;
        memset(used, 0, sizeof(used));
        return dfs(0, 0, 0);
}

int main() {
        int i;
        while ( (1 == scanf("%d", &cutnum)) && (0 < cutnum) ) {
                totlen = 0;
                for ( i = 0; i < cutnum; ++i ) {
                        scanf("%d", cutlen+i);
                        totlen += cutlen[ i ];
                }
                // 将木棍从长到短排序,然后从长到短搜索之
                qsort(cutlen, cutnum, sizeof(cutlen[0]), cmp);
                // 原始木棍长度必大于等于最长木棍长度,
                // 小于等于木棍总长度,
                // 从短到长枚举原始木棍长度。
                for ( orglen = cutlen[0]; (orglen < totlen) && (! find()); ++orglen ) {
                }
                printf("%d\n", orglen);
                //if ( orglen != totlen ) {
                //        for ( i = 0; i < cutnum; ++i ) {
                //                printf("--%d--", choice[ i ]);
                //        }
                //        printf("\n");
                //}
        }
        return 0;
}
*/



/**/ /**********************************************************
版本一:
EOJ  1981  TLE
POJ  1011  16MS  164K
HDOJ 1455  0MS   188K
UVA  307   TLE
*/

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

#define  L  70

int cutnum;                 // 木棍的数量
int cutlen[ L ], totlen;    // 木棍的各自长度,及总长度

int cmp( const void *pa, const void *pb) {
        return (*((int*)pb)) - (*((int*)pa));
}

int orglen, orgnum; // 原始木棍的长度及其数量
int used[ L ];      // dfs 时标记某木棍是否已经被用于组装原始木棍
//int choice[ L ];

        // 尝试拼装某原始木棍,
        // 此次尝试之前,
        // 已经使用了 t 根木棍,
        // 已经拼装出了原始木棍的部分长度,剩余 c 长度需要拼装,
        // 此次尝试,只需要从第 b 根木棍开始。
int dfs(int c, int b, int t) {
        int i, prelen = -1;
        if ( 0 == c ) {
                // 剩余长度为 0,则开始拼装下一根原始木棍,
                // 需扩大尝试的范围。
                for ( i = 0; (i < cutnum) && (used[i]); ++i ) {
                }
                // 所有木棍都已经被用于拼装原始木棍,
                // 即尝试成功。
                if ( i >= cutnum ) {
                        //printf("i >= cutnum, t = %d\n", t);
                        return 1;
                }
                used[ i ] = 1;
                //choice[ t ] = i;
                // 任一原始木棍中,必含有剩余木棍中最长的那根
                if ( dfs(orglen-cutlen[i], i+1, t+1) ) {
                        return 1;
                }
                used[ i ] = 0;
                return 0;
        }
        for ( i = b; i < cutnum; ++i ) {
                if (    (used[ i ]) ||          // 木棍没用过
                        (c < cutlen[i]) ||      // 剩余长度能容纳此木棍
                        (prelen == cutlen[i])   // 此长度的木棍已经试过,不行,无需再试
                ) {
                        continue;
                }
                used[ i ] = 1;
                //choice[ t ] = i;

                // 下次尝试从后面的木棍开始,前面的木棍无需考虑
                if ( dfs(c-cutlen[i], i+1, t+1) ) {
                        return 1;
                }
                prelen = cutlen[ i ];

                used[ i ] = 0;
        }
        return 0;
}

int find(){
        // 原始木棍长度必为总长度的约数
        if ( 0 != totlen % orglen ) {
                return 0;
        }
        orgnum = totlen / orglen;
        memset(used, 0, sizeof(used));
        return dfs(0, 0, 0);
}

int main() {
        int i;
        while ( (1 == scanf("%d", &cutnum)) && (0 < cutnum) ) {
                totlen = 0;
                for ( i = 0; i < cutnum; ++i ) {
                        scanf("%d", cutlen+i);
                        totlen += cutlen[ i ];
                }
                // 将木棍从长到短排序,然后从长到短搜索之
                qsort(cutlen, cutnum, sizeof(cutlen[0]), cmp);
                // 原始木棍长度必大于等于最长木棍长度,
                // 小于等于木棍总长度,
                // 从短到长枚举原始木棍长度。
                for ( orglen = cutlen[0]; (orglen < totlen) && (! find()); ++orglen ) {
                }
                printf("%d\n", orglen);
                //if ( orglen != totlen ) {
                //        for ( i = 0; i < cutnum; ++i ) {
                //                printf("--%d--", choice[ i ]);
                //        }
                //        printf("\n");
                //}
        }
        return 0;
}
*/

你可能感兴趣的:(ACM搜索题经典 Sticks)