搜索 和优化剪枝
来源:互联网 发布:mysql的大于等于符号 编辑:程序博客网 时间:2024/04/29 18:33
[USACO 4.1]Fence Rails
4.1中的奶牛家秘术和Fence Rails都是要加好几个剪枝才可以AC的问题
搜索对象:每个栅栏(rail)对应那个Board(可能的深度太大而答案很可能在很浅的地方,所以迭代深度去找(DFSID),如果某个深度找到一个可行解,立刻退出,再搜索下一个深度)
一般优化都可以从以下3点考虑:
1.可行性剪枝 如果我们预料到某种情况无论再怎么搜索也得不到可行解,那就直接退出
2.最优化剪枝 如果我们预料到某种情况无论再怎么走索也得不到比当前最优解更优的解,那也直接退出
3.搜索顺序 良好的搜索顺序会更快的找到答案
其他技巧因问题而不同
那么Fence Rails可以这样剪枝:
1.可行性: 如果当前剩下的不可用的Board(即不能再切出rails的board)的和比board总面积减去rails的总面积还大,那肯定不可行(通俗地讲就是不能再装Rails的面积比装满所有rails剩下来的面积大)
2.搜索顺序: 容易想到,如果刚开始的时候就用大的rail塞到较小的board中,很快就能找到可行解
3.注意有很多重复,考虑到重复的元素位置不同没有影响,所以如果上一个元素和该元素相同,那么所在的board就直接从上一个开始枚举
这样就可以AC了
我的代码(有些慢):
All tests OK.
Hal Burch 's Analysis:
Note that if there is a way to cut k rails, there is a way to cut the k shortest rails, so we will only consider subsets of the rails that contain the k shortest rails. Also, we know the sum or the rails cut cannot exceed the sum of the lengths of the rails, so we can stop our DFS-ID if it finds a way to cut the largest set of shortest rails such that the sum of the lengths of the rails is less than the sum of the board lengths.
Since finding a board from which to cut a longer rail is more difficult than finding a board for a shorter rail, wewill perform the search in such that the longest rail is cut first, then the second longest rail, etc.
Also, if two rails are of the same length, then cutting the first from board A and the second from board B is the same as cutting the first from board B and the second from board A, so within sets of rails of the same length, we will ensure that the rails are cut from boards of non-decreasing index.
If there are two boards of the same length, we need to check cutting the rail from only the first.
If there is a board of the same length of the rail, then cutting that rail from that board is optimal.
If, when cutting a board, we get a length of board less than the shortest rail, it is useless, and we can discard it from consideration. This reduces the total amount of board-feet left from which to cut the rest of the rails.
Another solution from Hassan Eslami:
USACO 4.1 Fence Rails 解题报告
这道题是一道经典的利用ID-DFS解决的一道搜索题目。开始做的时候看数据范围比较小,打算爆搜,发现第4个点就爆了。于是想改进方法。
1、二分枚举答案。可以用贪心法求出一个二分的下限,上限当然是rail的总个数。然后就是搜索对于当前的(l+r) div 2的深度(即可切得的rail的个数)是否可行。
2、排序。排序在这道题里面有相当大的用处。
首先说一下顺序,board是从大到小,rail是从小到大。这样做的好处是:可以将一些没有用的board和rail在一开始就剪掉。因为比最小的rail还小的board和比最大的board还大的rail都是没有用的。
其次,排序以后,可以利用有序的条件来规定搜索顺序。显然,board要从大到小用,rail要从小到大用,这样才能尽快到达目标状态。
3、如果当前可以砍出最小rail的board的总和比所有尚未砍出的rail的总和还要小------剪掉!
4、我们发现:rail一共有1023个,然而rail的最大值只有128,这就提示我们,有相当多的相同的rail。可以想到,如果rail[i]=rail[i+1],那么rail[i+1]所用的木料一定是rail[i]的后一个。
至此,每个点的时间都不超过0.1s 了。很爽吧 ^.^
/*
ID:alertya1
PROG:fence8
LANG:C++
*/
#include <fstream>
#include <algorithm>
#include <set>
using
namespace
std;
ifstream fin(
"fence8.in"
);
ofstream fout(
"fence8.out"
);
int
N,R,A[2048],Cf[60],Now[60],Pre[2048],Max,Sums,Limit,Tot;
bool
flag;
bool
Cmp(
int
a,
int
b)
{
return
a>b;
}
void
Initialize()
{
int
i;
fin>>N;
for
(i=1;i<=N;i++)
{
fin>>Cf[i];
Tot+=Cf[i];
}
sort(Cf+1,Cf+N+1);
fin>>R;
for
(i=1;i<=R;i++)
fin>>A[i];
sort(A+1,A+R+1);
for
(i=1;i<=R;i++)
if
(A[i]>Cf[N])
break
;
R=i-1;
for
(i=1;i<=R;i++)
Pre[i]=Pre[i-1]+A[i];
}
void
DFS(
int
pos,
int
Prev)
{
int
i,st,R=0;
if
(Sums>Tot-Pre[Limit])
return
;
if
(pos>Limit)
flag=
true
;
if
(flag)
return
;
st=1;
if
(A[Limit-pos+1]==A[Limit-pos+2])
st=Prev;
for
(i=st;i<=N;i++)
if
(A[Limit-pos+1]<=Cf[i])
{
Cf[i]-=A[Limit-pos+1];
if
(Cf[i]<A[1])
Sums+=Cf[i];
DFS(pos+1,i);
if
(Cf[i]<A[1])
Sums-=Cf[i];
Cf[i]+=A[Limit-pos+1];
}
}
int
main()
{
Initialize();
for
(Limit=1;Limit<=R;Limit++)
{
flag=
false
;
DFS(1,1);
if
(!flag)
break
;
}
fout<<Limit-1<<endl;
fin.close();
fout.close();
return
0;
}
当然,John可以切木板。因此,一个9英尺的木板可以切成一个5英尺和一个4英尺的木料 (当然也能切成3个3英尺的,等等)。John有一把(完美的)梦之锯,因此他在切木料时,不会有木料的损失。
所需要的栅栏长度可能会有重复(比如,一个3英尺和另一个3英尺长的栅栏可能同时都需要)。所需要的木料规格都已经给定。你不必切出更多木料,那没有用。
格式
PROGRAM NAME: fence8
INPUT FORMAT:
(file fence8.in)
第1行: N (1 <= N <= 50), 表示提供的木板的数目
第2行到第N+1行: N行,每行包括一个整数,表示各个木板的长度。
第N+2行: R (1 <= R <= 1023), 所需木料的数目
第N+3行到第N+R+2行: R行,每行包括一个整数(1 <= ri <= 128)表示所需木料的长度。
OUTPUT FORMAT:
(file fence8.out)
只有一行,一个数字,表示能切出的最多的所需木料的数目。当然,并不是任何时候都能切出所有所需木料。
SAMPLE INPUT
4304050251015161718192021252430 SAMPLE OUTPUT
7
USACO4.1 Fence rails DFSID
分类: USACO DFSID2012-04-07 22:58 149人阅读 评论(0) 收藏 举报rails优化searchgoogle所有题在A不掉之前都是难题,而当你把它A掉后,它便成了水题,而在你A掉它之前,你永远不知道它有多水。
这道题,我最先想到的是DP,觉得就是一个典型背包问题,不过是背包多了一点而已,然而再仔细一想N=50便意味着50个背包,128^50的运算量,不用细算也知道这样的程序跑出来太阳都熄火了,所以这样的DP是走不通的。想到这个section的主题是讲搜索优化的,那就试试搜索吧,直接DFS加剪枝?嗯,想了很久没想出来怎么做DFS,就在这个时候ZZY看完了刚更新的日剧,坐在旁边摇头晃脑满脸的意犹未尽,时不时往我这边漂上几眼,看到我一副苦逼的表情,点了几下头然后便笑而不语,过了许久才阴森的冒出一句话“这个要用DFSID”,我靠,什么是DFSID啊,完全不懂啊,听起来好高端啊...赶紧google之.....
搜索DFSID,wiki上没有,××没有,看了别人的博客,大致的感觉就是先确定一个界限再做DFS,而在这道题里面感觉倒有点像是枚举加DFS。在这个题中,用DFS找到能切割出的最大rail数不好怎么下手,但是,用DFS确定是否能切割出K块rail是相对容易的,所以我们就可以通过枚举出各个K值然后DFS得出能切割出的最大rail数,这里可以用二分的思想来做优化。
现在的主要问题就是如何DFS了。 既然是要确定一堆board能否切割出k块rail,我们不妨用一个全局的数组来储存各个board 的长度,用bool DFS(int k)来表示k块rail能否被切割出来,如果这堆board能切割出k块rail那么肯定能切割出k块最短的rail,所以,我们应该先给rail排个序。
朴素DFS代码:
这样没有做任何优化的DFS 只能过3个点,所以动手优化吧。
- bool DFS(int k)
- {
- if(k<0) return true;
- for(int i=0;i<boards;i++)
- {
- if(remain[i]>=rarr[k])//remain[i]记录了第i块board的剩余长度
- {
- remain[i]-=rarr[k];
- if(DFS(k-1)) return true;//先对rail排了序,rail从大到小搜
- remain[i]+=rarr[k];
- }
- }
- return false;
- }
在网上看了大牛们的博客,都说对于这样的切割问题,用剩余材料做优化可以减少大量的冗余搜索,其大致思路是这样的,如果boad的总长度是board_sum,需要切割出来的rail总长度是rail_sum,那么最大的可浪费材料是max_waste,如果某一种切割方式在切割完之前已产生了waste>max_waste的浪费,那么显然这种方式是不可行的。
优化后代码
- <pre name="code" class="cpp">bool DFS(int k)
- {
- if(k<0) return true;</pre><pre name="code" class="cpp"> //用浪费材料做剪枝</pre><pre name="code" class="cpp"> int waste=0;</pre><pre name="code" class="cpp"> for(int i=0;i<boards;i++) if(remain[i]<rarr[0]) waste+=remain[i];
- if(waste>max_waste) return false;
- for(int i=start;i<boards;i++)
- {
- if(remain[i]>=rarr[k])
- {
- if(k-1>=0 && rarr[k]==rarr[k-1]) ns=i;
- remain[i]-=rarr[k];
- if(DFS(k-1)) return true;
- remain[i]+=rarr[k];
- }
- }
- return false;
- }</pre><br>
- <pre></pre>
- <p></p>
- <pre></pre>
- <span style="white-space:pre"></span>优化后,竟然还是只能过3个点,太受打击了。
- <p></p>
- <p><span style="white-space:pre"></span>由于rail最多有1023块,而rail的长度是<=128的正整数,之意味着很多rail的长度是一样的,而对于长度一样的rail,它们的顺序是不重要的,所以可以做如下优化:</p>
- <p><span style="white-space:pre"></span>优化后代码</p>
- <p></p>
- <pre name="code" class="cpp">bool DFS(int k,int start)
- {
- if(k<0) return true;
- int waste=0;
- for(int i=0;i<boards;i++) if(remain[i]<rarr[0]) waste+=remain[i];
- if(waste>max_waste) return false;
- int ns=0;
- for(int i=start;i<boards;i++)
- {
- if(remain[i]>=rarr[k])
- {
- if(k-1>=0 && rarr[k]==rarr[k-1]) ns=i;
- else ns=0;
- remain[i]-=rarr[k];
- if(DFS(k-1,ns)) return true;
- remain[i]+=rarr[k];
- }
- }
- return false;
- }</pre><span style="white-space:pre"> </span>优化后提交,果断AC。
- <p></p>
- <p><span style="white-space:pre"></span>经实验发现,这两种优化缺少任何一种都只能过3个点,只有两种优化同时采用才能AC。</p>
- <p><span style="white-space:pre"></span>下面是完整的源代码:</p>
- <p></p>
- <pre name="code" class="cpp">#include<cstdio>
- #include<algorithm>
- using namespace std;
- FILE *in,*out;
- int barr[60],remain[60],rarr[1050],rarr_sum[1050],max_waste,board_sum,boards;
- int search(int s,int e);
- bool check(int k);
- bool DFS(int k,int start);
- int main()
- {
- in=fopen("fence8.in","r");
- out=fopen("fence8.out","w");
- int rails,i;
- fscanf(in,"%d",&boards);
- for(i=0;i<boards;i++) fscanf(in,"%d",&barr[i]);
- for(i=0;i<boards;i++) barr[i]=-barr[i];
- sort(barr,barr+boards);
- for(i=0;i<boards;i++) barr[i]=-barr[i];
- fscanf(in,"%d",&rails);
- for(i=0;i<rails;i++) fscanf(in,"%d",&rarr[i]);
- sort(rarr,rarr+rails);
- for(i=0;i<boards;i++) board_sum+=barr[i];
- for(i=0;i<rails;i++) rarr_sum[i]=rarr[i];
- for(i=1;i<rails;i++) rarr_sum[i]+=rarr_sum[i-1];
- max_result=0;
- for(i=1;i<rails;i*=2) if(!check(i)) break;
- if(i>rails) i=rails;
- fprintf(out,"%d\n",search(i>>1,i));
- fclose(in);
- fclose(out);
- return 0;
- }
- int search(int s,int e)
- {
- int mid=(s+e)>>1;
- if(e-s==1) mid=e;
- bool flag=check(mid);
- if(e-s==1 && flag) return e;
- if(e-s==1 && !flag) return s;
- if(e==s && flag) return e;
- if(flag) return search(mid,e);
- else return search(s,mid);
- }
- bool check(int k)
- {
- if(k<=0) return true;
- for(int i=0;i<boards;i++) remain[i]=barr[i];
- max_waste=board_sum-rarr_sum[k-1];
- if(max_waste<0) return false;
- return DFS(k-1,0);
- }
- bool DFS(int k,int start)
- {
- if(k<0) return true;
- int waste=0;
- for(int i=0;i<boards;i++) if(remain[i]<rarr[0]) waste+=remain[i];
- if(waste>max_waste) return false;
- int ns=0;
- for(int i=start;i<boards;i++)
- {
- if(remain[i]>=rarr[k])
- {
- if(k-1>=0 && rarr[k]==rarr[k-1]) ns=i;
- else ns=0;
- remain[i]-=rarr[k];
- if(DFS(k-1,ns)) return true;
- remain[i]+=rarr[k];
- }
- }
- return false;
- }
- </pre><br>
- <br>
- <p></p>
- 搜索 和优化剪枝
- 搜索方法中的剪枝优化
- 搜索算法的剪枝优化
- 谈搜索算法的剪枝优化(转载)
- 搜索的优化算法——剪枝
- Sticks ---- 深度优先搜索+剪枝优化
- 谈搜索算法的剪枝优化
- 搜索的优化算法——剪枝
- 【总结】搜索的剪枝二分预处理和离散化等优化
- BZOJ 1224: [HNOI2002]彩票 搜索,上下界剪枝,前缀和优化
- 搜索剪枝
- 搜索剪枝
- 搜索 剪枝
- 搜索剪枝
- 搜索剪枝
- 剪枝搜索
- 0.3poj2531(简单搜索技巧和剪枝)
- 搜索DFS+BFS和剪枝问题
- 多个config应用
- 完全卸载oracle11g步骤
- HR 模式
- coding
- 按A-Z中国城市列表排名
- 搜索 和优化剪枝
- POJ2318(计算几何)
- 调试jquery出现的错误:SCRIPT257: 由于出现错误80020101
- 成都传智播客JDBC视频及讲师介绍
- magento 小问题解决方案集
- winform文本编辑器的实现
- VBA连接SQL Server
- 对软件和读研的看法
- Ubuntu 中软件的安装、卸载以及查看的方法总结