codevs2777 栅栏的木料
农民John准备建一个栅栏来围住他的牧场。他已经确定了栅栏的形状,但是他在木料方面有些问题。当地的杂货储存商扔给John一些木板,而John必须从这些木板中找出尽可能多所需的木料。
当然,John可以切木板。因此,一个9英尺的木板可以切成一个5英尺和一个4英尺的木料 (当然也能切成3个3英尺的,等等)。John有一把(完美的)梦之锯,因此他在切木料时,不会有木料的损失。
所需要的栅栏长度可能会有重复(比如,一个3英尺和另一个3英尺长的栅栏可能同时都需要)。所需要的木料规格都已经给定。你不必切出更多木料,那没有用。
第1行: N (1 <= N <= 50), 表示提供的木板的数目
第2行到第N+1行: N行,每行包括一个整数,表示各个木板的长度。
第N+2行: R (1 <= R <= 1023), 所需木料的数目
第N+3行到第N+R+1行: R行,每行包括一个整数(1 <= ri <= 128)表示所需木料的长度。
只有一行,一个数字,表示能切出的最多的所需木料的数目。当然,并不是任何时候都能切出所有所需木料。
4
30
40
50
25
10
15
16
17
18
19
20
21
25
24
30
7
USACO译题 4.1.2
一、一个发现:
我们很容易就可以发现若能满足a块木料,则必能满足最小的a块木料;所以当我们将木料排序之后,被切出的木料将在一个前缀区间里,所以我们发现这个答案是具有单调性的,所以我的第一直觉是二分答案,判断前a块木料是否能被切出。
那么如何判断呢?我的第一直觉就是贪心。
二、尝试贪心:
1、策略:我的贪心策略是把最大的木料给能切出它的最小的木板切,直到木料用尽或不存在这样的木板为止。
2、反例:可是当我兴高采烈把程序交上去时,才发现它只能得75分。(竟然得了75分!)反例来自当最大的木料与最小的木板之差小于最小的木料时,它会造成冗余的浪费。如:用6,7切出2,3,3,5,这样贪心会得到3,虽然实际上它显然是4。
3、继续尝试,我们却发现我们想不到更好的贪心了。。
三、DFS:
于是我又开始尝试暴力搜索,但是在DFS的时候却忘了一得到的结论,实际上它能很好地缩小搜索树的形态的。
我当时的做法是:暴力枚举每个木料,令其被N个木板切或不切,直到搜出最优解。
想到的一个最优化剪枝是若当前剩余的可用木板(不小于尚未被切的最小木料)的总和小于更新更优解所需的木料,则剪枝;这样的话,需要我们按照木料从小到大的数据搜索。
这样的话。。只能得67分,还不如贪心呢。
当然。。这其实也很有可能是因为我犯了一个错误,就是实际上,我每次判断当前剩余可用木板实际上都应用O(n)的时间复杂度扫一遍才对,但我在这里。。犯了一个比较奇葩的错误,导致实际上削弱了这个剪枝的强度。
四、对DFS的改进:
1、来自一的改进,将N个木板升序排序,从第一个木板开始搜索,不存在不被切的状态了,这样搜索树的深度就是答案了。
2、来自二的改进,先贪出一个较优解,再用这个较优解做最优化剪枝。
3、来自三中的剪枝可以得以继续延用。
4、来自题解的剪枝:我们(题解)发现,这个题木料一共有1024块,但却落在[1,128]∩N的范围内,这说明有很多块木料会是一样的,DFS的时候会算出它们的排列,这浪费了我们大量时间却没有任何意义。所以我们干脆让它有序好了,即令若干块长度相同的木料被切的木板序号不下降或不上升即可。
5、但是有了上述四个剪枝以后,我们还是会TLE,所以题解又提供了一个蛋疼剪枝,就是将每一个状态的木板也排序,对于若干个相同的木板,只试切第一个即可,即若令剪枝3中序列不下降,就切编号最小的那个;若令剪枝3中序列不上升,就切编号最大的那个。当然实际上这个剪枝之所以够强,我想主要是由于数据的原因。
那么有了以上五个剪枝,我们是可以A掉这道题了的,但尤其是第四个剪枝,难免有卡数据的嫌疑。实际上,我们还有真正优秀的算法,并不需要这种来自数据的剪枝。
五、迭代加深搜索:
1、让我们继续沿用一中的思路,如果是二分+DFS判定呢?对!这样一来,搜索最优解就转变成了搜索可行解,这就为我们提供了新的优化方向。
2、四中改进1,2,3,4显然可以继续沿用,当然如果已贪出较优解就不需要再二分了,但这还是不够的,那么更牛的优化来自哪里呢?是来自搜索顺序的,搜索顺序对搜索最优解并不能造成太大的影响,但对于搜索可行解可就大不一样了,改变搜索顺序往往能起到非常惊人的效果。比如本题中,先切大木料显然要比先切小木料更优!这样的话我们发现剪枝3可以做某种程度的改动了,即其并不再需要每次都O(n)的扫描了,而是再切的时候判断剩余木料和最小木料的大小即可,因为最小木料一定是最后被切出来的。这样,究竟是使它增强了还是减弱了呢。。。还真不好说,毕竟搜索顺序都改变了。
3、但是这里同样需要注意一个问题,就是再搜索可行解的时候,需要中间强行return,这时千万不要忘了回溯,我在这个地方蛋疼了好久。
综,至此我们已经非常完美的解决本题了,即使是最大数据,运行时间也在0.004s.
#include<iostream>
using namespace std;
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
int ans,N,M,s[],a[],b[],sum;
inline bool dfs(int x,int pred){
if(!x)return ;
if(sum<s[x])return ;
sum-=b[x];
for(int i=x<ans&&b[x]==b[x+]?pred:;i<N;++i){
if(i<N&&a[i]==a[i+])continue;
if(a[i]>=b[x]){
a[i]-=b[x];
if(a[i]<b[])sum-=a[i];
if(dfs(x-,i)){
if(a[i]<b[])sum+=a[i];
sum+=b[x];
a[i]+=b[x];
return ;
}
if(a[i]<b[])sum+=a[i];
a[i]+=b[x];
}
}
sum+=b[x];
return ;
}
inline bool check(short m){
short x,minx,j,i,tmp[];
for(i=;i<N;++i)tmp[i]=a[i];
for(i=m;i;--i){
x=-,minx=0x7fff;
for(j=;j<N;++j)
if(tmp[j]>=b[i]&&tmp[j]<minx){
minx=tmp[j];
x=j;
}
if(x>-)tmp[x]-=b[i];
else return ;
}
return ;
}
char * ptr=(char *)malloc();
inline void in(int &x){
while(*ptr<''||*ptr>'')++ptr;
x=;
while(*ptr>&&*ptr<)x=x*+*ptr++-'';
}
int main(){
fread(ptr,,,stdin);
in(N);
int i;
for(i=;i<N;++i)in(a[i]),sum+=a[i];
in(M);
for(i=;i<=M;++i)in(b[i]);
sort(b+,b+M+);
sort(a,a+N);
b[M+]=-;
for(i=;i<=M;++i)s[i]=s[i-]+b[i];
short l=,r=M;
if(check(r)){
printf("%hd",r);
return ;
}
while(r-l>){
short m=(l+r)>>;
if(check(m))l=m;
else r=m;
}
ans=r;
while(ans<=M&&dfs(ans,N))++ans;
printf("%d",ans-);
}
codevs2777 栅栏的木料的更多相关文章
- USACO 4.1.2 栅栏的木料
这个讲的超好....一定要看...然后看我代码就好懂啦... http://blog.csdn.net/ta201314/article/details/41287567 各种优化确实非常好....搜 ...
- bzoj 1082: [SCOI2005]栅栏 题解
1082: [SCOI2005]栅栏 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 2340 Solved: 991[Submit][Status] ...
- Disruptor-NET和内存栅栏
Disruptor-NET算法(是一种无锁算法)需要我们自己实现某一种特定的内存操作的语义以保证算法的正确性.这时我们就需要显式的使用一些指令来控制内存操作指令的顺序以及其可见性定义.这种指令称为内存 ...
- SCOI2005栅栏
Description 农夫约翰打算建立一个栅栏将他的牧场给围起来,因此他需要一些特定规格的木材.于是农夫约翰到木材店购买木材.可是木材店老板说他这里只剩下少部分大规格的木板了.不过约翰可以购买这些木 ...
- GCD中的dispatch_barrier_async函数的使用(栅栏函数)
<一>什么是dispatch_barrier_async函数 毫无疑问,dispatch_barrier_async函数的作用与barrier的意思相同,在进程管理中起到一个栅栏的作用,它 ...
- bzoj1082[SCOI2005]栅栏
Description 农夫约翰打算建立一个栅栏将他的牧场给围起来,因此他需要一些特定规格的木材.于是农夫约翰到木材店购 买木材.可是木材店老板说他这里只剩下少部分大规格的木板了.不过约翰可以购买这些 ...
- BZOJ 1082 【SCOI2005】 栅栏
Description 农夫约翰打算建立一个栅栏将他的牧场给围起来,因此他需要一些特定规格的木材.于是农夫约翰到木材店购买木材.可是木材店老板说他这里只剩下少部分大规格的木板了.不过约翰可以购买这些木 ...
- 并发编程 04——闭锁CountDownLatch 与 栅栏CyclicBarrier
Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...
- thread_CyclicBarrier回环栅栏
CyclicBarrier回环栅栏,字面意思是可循环使用(Cyclic)的屏障(Barrier).通过它可以实现让一组线程等待至某个状态之后再全部同时执行. 它要做的事情是,让一组线程到达一个屏障(也 ...
随机推荐
- Linux一些经典书籍
一.linux之路 1.入门篇 <LINUX权威指南>书不错,写的很全面也比较广,涉及的不深,做为入门书籍不错,可以比较全面的了解linux .另外比较热门的也可以看看<鸟哥的私房菜 ...
- MultiThread
Stephen Toub From MicroSoft Crop. Stephen Cleary It's All About the SynchronizationContext How would ...
- NIO与AIO,同步/异步,阻塞/非阻塞
1.flip(),compact(),与clear()的使用 flip()内部实现,先将limit设为当前位置,再将缓冲区的postion设为0,所以是为将缓冲区的数据写出到其它通道或者get()作准 ...
- java设计模式之外观模式
外观模式概念 外观模式又称为门面模式,为子系统中的一组接口提供一个一致的界面,此模式定义了一个搞层次接口,使得这一个子系统更加容易使用.这一模式完美的体现了依赖倒转原则和迪米特法则的思想,所以是非常常 ...
- UVALive 4864 Bit Counting --记忆化搜索 / 数位DP?
题目链接: 题目链接 题意:如果一个数二进制n有k位1,那么f1[n] = k,如果k有s位二进制1,那么f2[n] = f1[k] = s. 如此往复,直到fx[n] = 1,此时的x就是n的”K ...
- sqlite常用查询
做的小工具用到了sqlite,在查询上较sqlserver还是稍有差异,将常用操作汇总一下,慢慢收集和整理. --查询版本SELECT sqlite_version() AS 'SQLite Vers ...
- mysql 加锁测试
今天研究cobar,做执行时间测试,需要对表记录加锁.用了以下两种方式为表记录加锁. 第一种方式: begin; //开始事务 select * from 表名 ( where ……) for ...
- 熟悉MyEclipse
资源网址:http://www.myeclipsecn.com/learningcenter/ 20151126 [从这里开始]量身打造自己的MyEclipse(多图) 主要讲在MyEclipse里面 ...
- 2016-2017-2《程序设计与数据结构》学生博客&git@OSC
2016-2017-2<程序设计与数据结构>学生博客&git@OSC 博客园 20162301张师瑜 20162302杨京典 20162303石亚鑫 20162304张浩林 201 ...
- ionic 开发笔记
1.AngularJS 外部的控制器(DOM 事件.外部的回调函数如 jQuery UI 空间等)调用了 AngularJS 函数之后,必须调用$apply.在这种情况下,你需要命令 AngularJ ...