POJ 1011 Sticks
2012-08-14 17:43
127 查看
地址:http://poj.org/problem?id=1011
问题描述:
乔治拿来一组等长的木棒,将它们随机地裁断,使得每一节木棒的长度都不超过50个长度单位。然后他又想把这些木棒恢复到为裁截前的状态,但忘记了木棒的初始长度。请你设计一个程序,帮助乔治计算木棒的可能最小长度。每一节木棒的长度都用大于零的整数表示
输入:由多个案例组成,每个案例包括两行。第一行是一个不超过64的整数,表示裁截之后共有多少节木棒。第二行是经过裁截后,所得到的各节木棒的长度。在最后一个案例之后,是零。
输出:为每个案例,分别输出木棒的可能最小长度。每个案例占一行。
思路1:
题意: 给你n 木棍碎片,要求还原原木棍,且原木棍等长,问最短长度是多少。
分析: 各种剪枝
思路2:
思想很简单,一个接一个的把木棍拼起来,最后把木棍用光。
* 关键的地方是几个剪枝技巧:
* 设所有木棍的总长度为 Sum, 最终的答案(长度)是 L。
* 1. 首先要明白, Sum一定要能被 L 整除。
* 2. L 一定 大于等于 题目给出的最长的木棍的长度 Max。
* 由上述两点,我们想到,可以从 Max 开始递增地枚举 L,
* 直到成功地拼出 Sum/L 支长度为 L 的木棍。
* 搜索种的剪枝技巧:
* 3. 将输入的输入从大到小排序,这么做是因为一支长度为 K
* 的完整木棍,总比几支短的小木棍拼成的要好。
* 形象一些:
* 如果我要拼 2 支长为8的木棍,第一支木棍我拼成
* 5 + 3
* 然后拼第二支木棍但是失败了,而我手中还有长为 2 和 1
* 的木棍,我可以用 5 + 2 + 1 拼好第一支,再尝试拼第二
* 支,仔细想一想,就会发现这样做没意义,注定要失败的。
* 我们应该留下 2+1 因为 2+1 比 3 更灵活。
* 4. 相同长度的木棍不要搜索多次, 比如:
* 我手中有一些木棍, 其中有 2 根长为 4 的木棍, 当前搜索
* 状态是 5+4+.... (即表示长度为 5,4,2 的三支拼在一起,
* ...表示深层的即将搜索的部分), 进行深搜后不成功,故我
* 没必要用另一个 4 在进行 5+4+...
* 5. 将开始搜索一支长为 L 的木棍时,我们总是以当前最长的未
* 被使用的 木棍开始,如果搜索不成功,那么以比它短的开始
* 那么也一定不能取得全局的成功。因为每一支题目给出的木棍
* 都要被用到。
* 如果,有
* 4
* 5 4 4 3 2
* 想拼成长为 6 的木棍,那么从 5 开始, 但是显然没有能与 5
* 一起拼成 6 的,那么我就没必要去尝试从 4 开始的,因为
* 最终 5 一定会被遗弃。在拼第 2 3 ... 支木棍时,一样。
* 6. 最后的最简单的一个就是,
* for(int i = 0; i < n; i++)
* for(int j = 0; j < n; j++)
* {}
* 与
* for(int i = 0; i < n; i++)
* for(int j = i+1; j < n; j++)
* {}
* 的区别,这个不多说了。
* 7. 我用过的另一个剪枝,但是对 poj 的数据效果一般,
* 用一个数组, Sum[i] 保存 第 i 个木棍之后,即比第 i 枝
* 木棍短或与之相等所有的木棍的长度之和。
* 试想,如果剩余的所有木棍加在一起都不能和我当前的状态拼
* 出一直长为 L 的木棍(从长度来看),还有必要搜下去么?
代码如下:
思路3:
1:初始状态:有N节木棒
最终状态:这N节木棒恰好被拼接成若干根等长的木棒
从初始状态到最终状态最多有多少条不同的“路径”?
Sum /maxParts。其中Sum是全部N节木棒的长度之和,maxParts是最长一节木棒的长度
每条“路径”对应一个木棒的长度。从木棒长度最小的那条可能“路径”开始,如果成功地的找到了这条“路径”,就解决了问题
2:构造一条木棒长度为L的“路径”:拼接木棒
在未被拼接的木棒中,找出一节最长的,开始拼接
从未拼接的木棒中,选择一个长度合适的木棒,使得拼接后的木棒长度≤L
找到了
在前面的拼接过程中曾试图用过相同长度的一节其他木棒,但发现这样拼接不成功,继续寻找能够进行拼接的木棒
把找到的那节木棒拼接上去。继续进行拼接
继续拼接成功,找到了“路径”
继续拼接不成功,把刚拼接的那节木棒拿下来,继续找下一个合适的未拼接木帮
没有找到:拼接失败
3:在已拼接部分未用长度为L1的木棒,则表明用长度为L1的木棒来拼接时是不成功的
下次拼接时也不能选择长度为L1的木棒,而应该选择长度为L2或L3的木棒
如果长度为L2或L3的木棒也不能选择,则需要替换已拼接部分的最后一节木棒
代码如下:
思路4:
问题描述:
乔治拿来一组等长的木棒,将它们随机地裁断,使得每一节木棒的长度都不超过50个长度单位。然后他又想把这些木棒恢复到为裁截前的状态,但忘记了木棒的初始长度。请你设计一个程序,帮助乔治计算木棒的可能最小长度。每一节木棒的长度都用大于零的整数表示
输入:由多个案例组成,每个案例包括两行。第一行是一个不超过64的整数,表示裁截之后共有多少节木棒。第二行是经过裁截后,所得到的各节木棒的长度。在最后一个案例之后,是零。
输出:为每个案例,分别输出木棒的可能最小长度。每个案例占一行。
思路1:
题意: 给你n 木棍碎片,要求还原原木棍,且原木棍等长,问最短长度是多少。
分析: 各种剪枝
/*1:越长的木棍对后面木棍的约束力越大,因此要把小木棍排序, 按木棍长度从大到小搜索,这样就能在尽可能靠近根的地方 剪枝。 :当出现加上某根木棍恰好能填满一根原始木棍,但由在后面的 搜索中失败了,就不必考虑其他木棍了,直接退出当前的枚举。 :考虑每根原始木棍的第一根木棍,如果当前枚举的木棍长度无 法得出合法解,就不必考虑下一根木棍了,当前木棍一定是作 为某根原始木棍的第一根木棍的,现在不行,以后也不可能得 出合法解。也就是说每根原始木棍的第一根小木棍一定要成 功,否则就返回。 :剩下一个通用的剪枝就是跳过重复长度的木棍,当前木棍跟 它后面木棍的无法得出合法解,后面跟它一样长度的木棍也 不可能得到合法解,因为后面相同长度木棍能做到的,前面这 根木棍也能做到。 */ #include<stdio.h> #include<string.h> #include<stdlib.h> int cmp(const void*p1,const void*p2) { return *(int*)p2-*(int*)p1; } int a[100],v[100]; int n,len,s,tot,min; bool dfs(int k,int mi,int left) { int i; if(left==min) return 1; for(i=k;i<=n;i++) if(!v[i]&&a[i]<=mi) { v[i]=1; if(a[i]==mi&&dfs(1,len,left-a[i])) return 1; else if(dfs(i+1,mi-a[i],left-a[i])) return 1; v[i]=0; if(a[i]==min)return 0; if(left==tot)return 0; if(mi==len) return 0; while(a[i+1]==a[i]) i++; } return 0; } int main() { int i,res; while(scanf("%d",&n),n) { tot=0; for(i=1;i<=n;i++){ scanf("%d",&a[i]); tot+=a[i]; } qsort(a+1,n,sizeof(a[0]),cmp); len=a[1]; res=len; memset(v,0,sizeof(v)); res=tot; for(;len<tot;len++) if(tot%len==0&&dfs(1,len,tot)) { res=len; break; } printf("%d\n",res); } return 0; }
思路2:
思想很简单,一个接一个的把木棍拼起来,最后把木棍用光。
* 关键的地方是几个剪枝技巧:
* 设所有木棍的总长度为 Sum, 最终的答案(长度)是 L。
* 1. 首先要明白, Sum一定要能被 L 整除。
* 2. L 一定 大于等于 题目给出的最长的木棍的长度 Max。
* 由上述两点,我们想到,可以从 Max 开始递增地枚举 L,
* 直到成功地拼出 Sum/L 支长度为 L 的木棍。
* 搜索种的剪枝技巧:
* 3. 将输入的输入从大到小排序,这么做是因为一支长度为 K
* 的完整木棍,总比几支短的小木棍拼成的要好。
* 形象一些:
* 如果我要拼 2 支长为8的木棍,第一支木棍我拼成
* 5 + 3
* 然后拼第二支木棍但是失败了,而我手中还有长为 2 和 1
* 的木棍,我可以用 5 + 2 + 1 拼好第一支,再尝试拼第二
* 支,仔细想一想,就会发现这样做没意义,注定要失败的。
* 我们应该留下 2+1 因为 2+1 比 3 更灵活。
* 4. 相同长度的木棍不要搜索多次, 比如:
* 我手中有一些木棍, 其中有 2 根长为 4 的木棍, 当前搜索
* 状态是 5+4+.... (即表示长度为 5,4,2 的三支拼在一起,
* ...表示深层的即将搜索的部分), 进行深搜后不成功,故我
* 没必要用另一个 4 在进行 5+4+...
* 5. 将开始搜索一支长为 L 的木棍时,我们总是以当前最长的未
* 被使用的 木棍开始,如果搜索不成功,那么以比它短的开始
* 那么也一定不能取得全局的成功。因为每一支题目给出的木棍
* 都要被用到。
* 如果,有
* 4
* 5 4 4 3 2
* 想拼成长为 6 的木棍,那么从 5 开始, 但是显然没有能与 5
* 一起拼成 6 的,那么我就没必要去尝试从 4 开始的,因为
* 最终 5 一定会被遗弃。在拼第 2 3 ... 支木棍时,一样。
* 6. 最后的最简单的一个就是,
* for(int i = 0; i < n; i++)
* for(int j = 0; j < n; j++)
* {}
* 与
* for(int i = 0; i < n; i++)
* for(int j = i+1; j < n; j++)
* {}
* 的区别,这个不多说了。
* 7. 我用过的另一个剪枝,但是对 poj 的数据效果一般,
* 用一个数组, Sum[i] 保存 第 i 个木棍之后,即比第 i 枝
* 木棍短或与之相等所有的木棍的长度之和。
* 试想,如果剩余的所有木棍加在一起都不能和我当前的状态拼
* 出一直长为 L 的木棍(从长度来看),还有必要搜下去么?
代码如下:
#include <iostream> #include <string.h> #include <stdio.h> #include <algorithm> using namespace std; int sticks[65],n,sum,num,l;//l最小的与木棒的长度 num:圆木棒的个数 sum:圆木棒的总长度 bool mark[65]; bool cmp(int a,int b) { return a>b; } //s:已经组成的小木棒的个数,le:当前搜索时正在组成的小木条的长度。pos:要组合的小木条的下标位置 bool dfs(int s,int le,int pos) { int i; bool sign = (le == 0?true:false); if(s==num)return true; for(i = pos + 1;i < n;i++) { if(mark[i])continue;//如果这个小木棒组合过则从下一个开始 if(le + sticks[i]==l)//如果组合刚好组成l长度的木棒那么就要组合下一根木棒了即第s+1根 { mark[i] = true; if(dfs(s+1,0,-1))//第s根已经组合成功了组合下一根 return true; mark[i] = false; return false;//如果组合失败了那么就要返回false 而且这个木棒的状态应还原为没组合过 } else if(le + sticks[i]<l) //如果组合当前这根后长度仍然小于l那么从i开始往下找继续组合第S根木棒 { mark[i] = true; if(dfs(s,le+sticks[i],i))//如果可以组成就返回true return true; mark[i] = false; if(sign)return false; while(sticks[i]==sticks[i+1])i++; } } return false; } int main() { while(scanf("%d",&n)!=EOF,n) { sum = 0; for(int i = 0; i < n; i++) { scanf("%d",&sticks[i]); sum += sticks[i]; } sort(sticks,sticks+n,cmp);//将木棒从大到小排序 for(l = sticks[0]; l <= sum; l++)//从最大的开始搜索 { if(sum%l==0)//如果该长度可以被整除那么可能是结果,否则继续 { num = sum/l;//num:记录该情况下可以组成的木棒的数目。 memset(mark,false,sizeof(mark));//每种情况都要进行初始化,把所有的木棒的使用状态设为false if(dfs(1,0,-1))//当返回true时说明搜索成功了,可以组成该长度木条 { printf("%d\n",l); break; } } } } return 0; }
思路3:
1:初始状态:有N节木棒
最终状态:这N节木棒恰好被拼接成若干根等长的木棒
从初始状态到最终状态最多有多少条不同的“路径”?
Sum /maxParts。其中Sum是全部N节木棒的长度之和,maxParts是最长一节木棒的长度
每条“路径”对应一个木棒的长度。从木棒长度最小的那条可能“路径”开始,如果成功地的找到了这条“路径”,就解决了问题
2:构造一条木棒长度为L的“路径”:拼接木棒
在未被拼接的木棒中,找出一节最长的,开始拼接
从未拼接的木棒中,选择一个长度合适的木棒,使得拼接后的木棒长度≤L
找到了
在前面的拼接过程中曾试图用过相同长度的一节其他木棒,但发现这样拼接不成功,继续寻找能够进行拼接的木棒
把找到的那节木棒拼接上去。继续进行拼接
继续拼接成功,找到了“路径”
继续拼接不成功,把刚拼接的那节木棒拿下来,继续找下一个合适的未拼接木帮
没有找到:拼接失败
3:在已拼接部分未用长度为L1的木棒,则表明用长度为L1的木棒来拼接时是不成功的
下次拼接时也不能选择长度为L1的木棒,而应该选择长度为L2或L3的木棒
如果长度为L2或L3的木棒也不能选择,则需要替换已拼接部分的最后一节木棒
代码如下:
1 #include <iostream.h> 2 #include <memory.h> 3 #include <stdlib.h> 4 int T, S; 5 int L; 6 int anLength[65]; 7 int anUsed[65]; 8 int i,j,k; 9 int Dfs(int nUnusedSticks, int nLeft); 10 int MyCompare( const void * e1, const void * e2) { 11 int * p1, * p2; 12 p1 = (int * ) e1; 13 p2 = (int * ) e2; 14 return * p2 - * p1; 15 } 16 main() 17 { 18 while(1) { 19 cin >> S; 20 if( S == 0 ) 21 break; 22 int nTotalLen = 0; 23 for( int i = 0; i < S; i ++ ) { 24 cin >> anLength[i]; 25 nTotalLen += anLength[i]; 26 } 27 qsort(anLength,S,sizeof(int),MyCompare); 28 for( L = anLength[0]; L <= nTotalLen / 2; L ++ ) { 29 if( nTotalLen % L) 30 continue; 31 memset( anUsed, 0,sizeof(anUsed)); 32 if( Dfs( S,L)) { 33 cout << L << endl; 34 break; 35 } 36 } 37 if( L > nTotalLen / 2 ) 38 cout << nTotalLen << endl; 39 } // while 40 } 41 int Dfs( int nUnusedSticks, int nLeft) 42 // nLeft表示当前正在拼的棍子和 L 比还缺的长度 43 { 44 if( nUnusedSticks == 0 && nLeft == 0 ) 45 return true; 46 if( nLeft == 0 ) //一根刚刚拼完 47 nLeft = L; //开始拼新的一根 48 for( int i = 0;i < S;i ++) { 49 if( !anUsed[i] && anLength[i] <= nLeft) { 50 if( i > 0 ) { 51 if( anUsed[i-1] == false 52 && anLength[i] == anLength[i-1]) 53 continue; //剪枝3 54 } 55 anUsed[i] = 1; 56 if ( Dfs( nUnusedSticks - 1, 57 nLeft - anLength[i])) 58 return true; 59 else { 60 anUsed[i] = 0;//说明不能用i作为 61 //第1条, 62 //那么要拆以前的 63 //棍子,i还可能用 64 //在以前的棍子里, 65 //因此要 anUsed[i] = 0; 66 if( anLength[i] == nLeft || nLeft == L) 67 return false;//剪枝2、1 68 } 69 } 70 } 71 return false; 72 }
思路4:
#include <stdio.h> #include <stdlib.h> int sticks[100],n; bool used[100]; int cmp(const void *x,const void *y) { return *(int *)y - *(int *)x; } bool find(int left,int num,int len) { int i; if(left == 0 && num == 0) return 1; if(left == 0) left = len; for(i = 0;i < n;i ++) { if(sticks[i] <= left && !used[i]) { used[i] = 1; if(find(left - sticks[i],num-1,len)) return 1; used[i] = 0; if(sticks[i] == left || left == len) return 0; } } return 0; } int main() { int i,sum = 0; while(scanf("%d",&n) != EOF && n) { sum = 0; for(i = 0;i < n;i ++) { scanf("%d",&sticks[i]); sum += sticks[i]; used[i] = 0; } qsort(sticks,n,sizeof(int),cmp); for(i = sticks[0];i <= sum;i ++) { if((sum % i == 0) && find(i,n,i)) { printf("%d\n",i); break; } } } return 0; }
相关文章推荐
- poj 1011 Sticks(dfs+剪枝)
- poj1011 Sticks
- POJ 1011 Sticks
- POJ1011 Sticks
- POJ 百练1011 STICKS
- poj 1011-小白算法练习 Sticks 从最简单的做起 dfs+剪枝
- Poj_1011_Sticks(剪枝)
- poj 1011 Sticks
- TLE: poj 1011 Sticks
- poj 1011 Sticks(经典搜索问题:DFS+剪枝)
- poj 1011 Sticks
- poj 1011 Sticks (DFS+剪枝)
- Poj_1011_Sticks(剪枝)
- HDU 1455 POJ 1011 UVA 307 Sticks
- [POJ 1011] Sticks DFS神剪枝
- HDU 1455 POJ 1011 Sticks 搜索
- poj 1011 sticks
- POJ 1011 Sticks
- POJ 1011--Sticks
- poj1011 sticks