经典问题:木棒(搜索+强力剪枝)
来源:互联网 发布:网络招生咨询技巧 编辑:程序博客网 时间:2024/05/08 22:39
Memory Limit: 10000KTotal Submissions: 95421
Accepted: 21444
Description
Input
Output
Sample Input
95 2 1 5 2 1 5 2 141 2 3 40
Sample Output
65
Source
Translator
==================================================================
这曾经是我省的省选题,当初做的时候一直没A,今天刷题库看到了,搜了下相关内容,终于搞懂了~
==================================================================
资料(来自http://blog.163.com/xdu_cfcry/blog/static/1694623032010718274132/):
==================================================================
经典搜索题,黑书上的剪枝例题。剪枝的空间很大,剪枝前写下朴素的搜索框架(黑字部分),枚举原始木棍的长度及由那些小木棍组合。原始木棍长度一定是小木棍总长度的约数,因此可以减少枚举量。
越长的木棍对后面木棍的约束力越大,因此要把小木棍排序,按木棍长度从大到小搜索,这样就能在尽可能靠近根的地方剪枝。(剪枝一)
如果当前木棍能恰好填满一根原始木棍,但因剩余的木棍无法组合出合法解而返回,那么让我们考虑接下来的两种策略,一是用更长的木棍来代替当前木棍,显然这样总长度会超过原始木棍的长度,违法。二是用更短的木棍组合来代替这根木棍,他们的总长恰好是当前木棍的长度,但是由于这些替代木棍在后面的搜索中无法得到合法解,当前木棍也不可能替代这些木棍组合出合法解。因为当前木棍的做的事这些替代木棍也能做到。所以,当出现加上某根木棍恰好能填满一根原始木棍,但由在后面的搜索中失败了,就不必考虑其他木棍了,直接退出当前的枚举。(剪枝二)
显然最后一根木棍是不必搜索的,因为原始木棍长度是总木棍长度的约数。(算不上剪枝)
考虑每根原始木棍的第一根木棍,如果当前枚举的木棍长度无法得出合法解,就不必考虑下一根木棍了,当前木棍一定是作为某根原始木棍的第一根木棍的,现在不行,以后也不可能得出合法解。也就是说每根原始木棍的第一根小木棍一定要成功,否则就返回。(剪枝四)
剩下一个通用的剪枝就是跳过重复长度的木棍,当前木棍跟它后面木棍的无法得出合法解,后面跟它一样长度的木棍也不可能得到合法解,因为后面相同长度木棍能做到的,前面这根木棍也能做到。(剪枝五)
这样剪枝基本就结束了,我们发现,每种剪枝都只是加了一条语句,但剪枝效果非常明显。uva307题目跟这个一模一样,当时uva的数据规模更强,许多在poj 10+MS的程序在uva 3000MS都跑不出来,当时我的加上这些剪枝,我的程序在uva也能跑出靠前的成绩。 rank:38 time:0.244s
剪枝要平衡准确性和额外花费的关系,一开始我用上下界剪枝,这个额外花费相当大,每次搜索一根原始木棍都要更新从某根木棍开始到最后一根可用木棍的总长度,对于一般的数据确实能跑的比没加剪枝快,但对苛刻的,第一根木棍总不成功的数据,这个剪枝就成了程序的瓶颈,导致我在poj都超时了,删除后0MS, 汗~~。
#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;int used[100],len[100],sum,n,Min,s[100];int find(int p,int rest,int trest){ int i; if (trest==Min) return 1; for (i=p;i<=n;i++) if (!used[i]&&len[i]<=rest) { used[i]=1; if (len[i]==rest) { if (find(1,Min,trest-len[i])) return 1; } else if (find(i+1,rest-len[i],trest-len[i])) return 1; used[i]=0; if (len[i]==rest) return 0; if (trest==sum) return 0; if (rest==Min) return 0; while (len[i+1]==len[i]) i++; } return 0;}bool cmp(int a,int b) {return a>b;}int main(){ int i; while (scanf("%d",&n)&&n!=0) { for (i=1;i<=n;i++) scanf("%d",&len[i]); memset(used,0,sizeof(used)); sort(len+1,len+n+1,cmp); sum=0; for (i=1;i<=n;i++) sum+=len[i]; Min=len[1]; while (sum%Min!=0) Min++; s[n+1]=0; while (find(1,Min,sum)==0) { Min++; while (sum%Min!=0) Min++; } printf("%d\n",Min); } return 0;}==================================================================
看完这个我终于会了,有些强力剪枝真是想不到啊~
==================================================================
贴个代码:
varn,m,tot,i:longint;a:array[1..65] of longint;v:array[1..65] of boolean;function dfs(nn,p,k,total:longint):boolean;var i:longint;begin if nn=0 then exit(true); i:=1; while i<=n do begin if (not v[i])and(a[i]+total<=m) then begin v[i]:=true; if a[i]+total=m then dfs:=dfs(nn-1,p+1,1,0) else dfs:=dfs(nn-1,p,k+1,total+a[i]); v[i]:=false; if dfs then exit(true); if (a[i]+total=m)or(k=1) then exit(false); while(i<n)and(a[i+1]=a[i])do inc(i); end; inc(i); end; exit(false);end;procedure sort(l,r:longint);var i,j,mid,tmp:longint;begin i:=l; j:=r; mid:=a[(l+r)shr 1]; repeat while a[i]>mid do inc(i); while a[j]<mid do dec(j); if i<=j then begin tmp:=a[i]; a[i]:=a[j]; a[j]:=tmp; inc(i); dec(j); end; until i>j; if i<r then sort(i,r); if l<j then sort(l,j);end;begin while true do begin readln(n); if n=0 then break; tot:=0; for i:=1 to n do begin read(a[i]); inc(tot,a[i]); end; sort(1,n); fillchar(v,sizeof(v),false); for m:=a[1] to tot do if tot mod m =0 then if dfs(n,1,1,0) then begin writeln(m); break; end; end;end.
- 经典问题:木棒(搜索+强力剪枝)
- poj1011 stick 强力搜索剪枝
- 木棒拼接 poj1011 搜索+剪枝 递归实现
- poj 1011 Sticks(经典搜索问题:DFS+剪枝)
- poj 1011 回溯+剪枝 木棒问题
- POJ 1011 最小的木棒 (dfs+剪枝|| 搜索好题)
- bzoj1306(搜索与剪枝经典)
- 2817:木棒 【递归+剪枝】
- poj1011 木棒 dfs+剪枝
- POJ 1011 Sticks (DFS +强力剪枝 (经典))
- poj 1011 sticks(木棒) (dfs+剪枝)
- 搜索剪枝问题
- POJ-1011-经典搜索题(深搜+剪枝)
- 经典搜索(深搜+剪枝+Java细节注意)
- 木棒问题
- 木棒问题
- 木棒问题
- 木棒问题
- Android ADT 17.0 Build Path的问题
- 使用testFlight进行企业部署
- linux嵌入式系统开发之触摸屏---驱动篇(中/Linux输入子系统)
- Android类参考---Message
- c语言中判断一个字符串是否包含另一个字符串
- 经典问题:木棒(搜索+强力剪枝)
- 获取重叠Marker信息
- linux嵌入式系统开发之触摸屏---驱动篇(下/源码分析)
- QString 乱谈(1)
- linux内核驱动中_IO, _IOR, _IOW, _IOWR 宏的用法与解析
- jquery radio取值,checkbox取值,select取值,radio选中,checkbox选中,select选中,及其相关 .
- 一句代码
- javascript坐标:event.x、event.clientX、event.offsetX、event.screenX
- Linux嵌入式系统开发之Led开发---驱动篇