首页 > 代码库 > Sticks(poj1011/uva307)
Sticks(poj1011/uva307)
题目大意:
乔治有一些碎木棒,是通过将一些相等长度的原始木棒折断得到的,给出碎木棒的总数和各自的长度,求最小的可能的原始木棒的长度;(就是将一些正整数分组,每组加起来和相等,使和尽可能小)
一开始做poj 32ms过,但uva3000 ms 都超时。。。而且poj discuss里给出了一组bT数据,最后uva 0.2sac的代码也跑了3 。4秒左右。discuss里的大牛据说什么奇偶性剪枝0.01ms过,可惜搜了半天也没找到具体方法。这题就这样过好了。。(参考各种空间博客才艰难ac。不过此题实在是太经典了,搜索学习剪枝必做)
对于这道题而言,剪枝的策略一般有下面6个:
①先将木棒长度从大到小进行排序,这样便于后面的选择和操作,是后面一些剪枝算法的前提。
②在枚举原木棒长度时,枚举的范围为max与sum/2之间,如果这个区间内没有找到合适的长度,那么最后原木棒的长度只能是sum。
③枚举的原木棒的长度只能是sum的约数。
④在深搜过程中,如果当前木棒和前一个木棒的长度是一样的,但是前一个木棒没有被选上,那么这个木棒也一定不会被选上。
⑤在深搜过程中,如果当前是在拼一根新木棒的第一截,但如果把可用的最长的一根木棒用上后不能拼成功的话,那么就不用再试后面的木棒了,肯定是前面拼的过程出了问题。
⑥在深搜过程中,如果当前可用的木棒恰好能补上一根原木棒的最后一截,但用它补上之后却不能用剩下的木棒完成后续的任务,那么也不用再试后面的木棒了,肯定是前面拼的过程出了问题。
参考博客:http://www.cnblogs.com/staginner/archive/2011/09/08/2171329.html
我的ac 代码:(剪枝见注释)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int n,max_len,min_len,sum,ans,num,flag; int a[10000],v[10000]; bool cmp(const int &x,const int &y) { return !(x<y); } void search(int step,int from,int cur)//step表示当前去凑的是第几根原始棒,order表示当前用了多少根碎棒子来凑第step根 { //printf("%d %d\n",step,cur); if (cur==ans) { if (step==num) { flag=1; return; } int k; for (k=1;k<=n && v[k];k++); v[k]=1; search(step+1,1,a[k]); v[k]=0; return; } for (int i=from;i<=n;i++) { if (v[i]) continue; if (cur+a[i]>ans) continue; if (a[i]==a[i-1] && !v[i-1]) continue;//剪枝3:上一根棒和目前这根一样并且没用过,那么用这一根去凑和用上一根去凑结果一样都不能出解 //printf("kk %d %d\n",cur,cur+a[i]); int t=a[i]+cur; if (t!=ans && t+min_len>ans) continue; v[i]=1; search(step,i+1,t); v[i]=0; if (t==ans) return; if (flag || cur==0)//剪枝4:如果当前用了1根碎棒子来凑,枚举了第一根碎棒不能出解那么跳出 return; } return; } int main () { while (scanf("%d",&n)==1) { if (n==0) break; v[0]=1; a[0]=210000000; max_len=-1; min_len=21000000; sum=0; flag=0; for (int i=1;i<=n;i++) { scanf("%d",&a[i]); if (a[i]>max_len) max_len=a[i]; if (a[i]<min_len) min_len=a[i]; sum+=a[i]; } sort(a+1,a+n+1,cmp); for (ans=max_len;ans<=sum/2;ans++)//剪枝1:答案的下界为最长的棒,上界为长度总和 { if (sum%ans!=0) continue;//剪枝 2:原始棒长度是总长度的约数 for (int k=1;k<=n;k++) v[k]=0; num=sum/ans; search(1,1,0); if (flag) { printf("%d\n",ans); break; } } if (!flag) printf("%d\n",sum); } return 0; } |