codevs2777 栅栏的木料

题目描述 Description

农民John准备建一个栅栏来围住他的牧场。他已经确定了栅栏的形状,但是他在木料方面有些问题。当地的杂货储存商扔给John一些木板,而John必须从这些木板中找出尽可能多所需的木料。

当然,John可以切木板。因此,一个9英尺的木板可以切成一个5英尺和一个4英尺的木料 (当然也能切成3个3英尺的,等等)。John有一把(完美的)梦之锯,因此他在切木料时,不会有木料的损失。

所需要的栅栏长度可能会有重复(比如,一个3英尺和另一个3英尺长的栅栏可能同时都需要)。所需要的木料规格都已经给定。你不必切出更多木料,那没有用。

输入描述 Input Description

第1行: N (1 <= N <= 50), 表示提供的木板的数目

第2行到第N+1行: N行,每行包括一个整数,表示各个木板的长度。

第N+2行: R (1 <= R <= 1023), 所需木料的数目

第N+3行到第N+R+1行: R行,每行包括一个整数(1 <= ri <= 128)表示所需木料的长度。

输出描述 Output Description

只有一行,一个数字,表示能切出的最多的所需木料的数目。当然,并不是任何时候都能切出所有所需木料。

样例输入 Sample Input

4
30
40
50
25
10
15
16
17
18
19
20
21
25
24
30

样例输出 Sample Output

7

数据范围及提示 Data Size & Hint

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[2000],a[100],b[2000],sum;
inline bool dfs(int x,int pred){
    if(!x)return 1;
    if(sum<s[x])return 0;
    sum-=b[x];
    for(int i=x<ans&&b[x]==b[x+1]?pred:0;i<N;++i){
        if(i<N&&a[i]==a[i+1])continue;
        if(a[i]>=b[x]){
            a[i]-=b[x];
            if(a[i]<b[1])sum-=a[i];
            if(dfs(x-1,i)){
                if(a[i]<b[1])sum+=a[i];
                sum+=b[x];
                a[i]+=b[x];
                return 1;
            }
            if(a[i]<b[1])sum+=a[i];
            a[i]+=b[x];
        }
    }
    sum+=b[x];
    return 0;
}
inline bool check(short m){
    short x,minx,j,i,tmp[104];
    for(i=0;i<N;++i)tmp[i]=a[i];
    for(i=m;i;--i){
        x=-1,minx=0x7fff;
        for(j=0;j<N;++j)
            if(tmp[j]>=b[i]&&tmp[j]<minx){
                minx=tmp[j];
                x=j;
            }
        if(x>-1)tmp[x]-=b[i];
        else return 0;
    }
    return 1;
}
char * ptr=(char *)malloc(1000000);
inline void in(int &x){
    while(*ptr<‘0‘||*ptr>‘9‘)++ptr;
    x=0;
    while(*ptr>47&&*ptr<58)x=x*10+*ptr++-‘0‘;
}
int main(){
    fread(ptr,1,1000000,stdin);
    in(N);
    int i;
    for(i=0;i<N;++i)in(a[i]),sum+=a[i];
    in(M);
    for(i=1;i<=M;++i)in(b[i]);
    sort(b+1,b+M+1);
    sort(a,a+N);
    b[M+1]=-1;
    for(i=1;i<=M;++i)s[i]=s[i-1]+b[i];
    short l=0,r=M;
    if(check(r)){
        printf("%hd",r);
        return 0;
    }
    while(r-l>1){
        short m=(l+r)>>1;
        if(check(m))l=m;
        else r=m;
    }
    ans=r;
    while(ans<=M&&dfs(ans,N))++ans;
    printf("%d",ans-1);
}
时间: 2024-10-07 19:12:17

codevs2777 栅栏的木料的相关文章

USACO 4.1.2 栅栏的木料

这个讲的超好....一定要看...然后看我代码就好懂啦... http://blog.csdn.net/ta201314/article/details/41287567 各种优化确实非常好....搜索的一道好题... 挂代码: /* Problem : USACO 4.1.2 栅栏的木料 Author : Robert Yuan 优化解释: 0. 二分+贪心判断可行解 (根据自己设计的贪心算法尽量的得到一个比较靠近正确值的 ans,再后面的搜索的下界就会大大提高) 1. 如果使用的木料与上一块

BZOJ1082: [SCOI2005]栅栏

1082: [SCOI2005]栅栏 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1906  Solved: 816[Submit][Status][Discuss] Description 农夫约翰打算建立一个栅栏将他的牧场给围起来,因此他需要一些特定规格的木材.于是农夫约翰到木材店购买木材.可是木材店老板说他这里只剩下少部分大规格的木板了.不过约翰可以购买这些木板,然后切割成他所需要的规格.而且约翰有一把神奇的锯子,用它来锯木板,不会产生任

FFT造成的频谱混叠,栅栏效应,频谱泄露,谱间干扰

fft在分析频谱分析的时候,会有下面四个方面的误差: (1)频谱混叠: 奈奎斯特定理已被众所周知了,所以几乎所有人的都知道为了不让频谱混叠,理论上采样频谱大于 等于信号的最高频率.那和时域上联系起来的关系是什么呢? 采样周期的倒数是频谱分辨率,最高频率的倒数是采样周期. 设定采样点数为N,采样频率fs,最高频率fh,故频谱分辨率f=fs/N,而fs>=2fh,所以可以看出最 高频率与频谱分辨率是相互矛盾的,提高频谱分辨率f的同时,在N确定的情况下必定会导致最高频 率fh的减小:同样的,提高最高频

把鞋子扔过栅栏

认识他是在一个知青聚会的饭局上. 当时,大家众星捧月般围住他讨要成功的秘笈.其实,刚步入社会时,他和大家一样上山下乡,接受贫下中农的再教育:在知青大返城的洪流中,他同样被一浪打进一家国营厂子里当工人.所不同的是,改革开放之初,他不安于现状,是全县第一个辞去公职,裸身下海去广东捞世界的人.经过30来年的商海打拼,如今,他是广州制鞋业的龙头老大,净资产超过1个亿. 酒至半酣,他娓娓给我们讲了一件他亲历的事:那年我在乡下,一群小男孩要经过一片栅栏拦路的另一边拾麦穗,由于栅栏很高,他们虽然一再努力,却还

图形填充之栅栏填充算法

编译器:VS2013 该算法相对边缘填充算法莱说,效率较高来说,选取一个顶点的横坐标为栅栏,将直线和栅栏之间进行填充,如果颜色为背景色,则填充填充色,否则则填充背景色 代码: 1 // 栅栏填充算法.cpp : 定义控制台应用程序的入口点. 2 // 3 4 #include "stdafx.h" 5 #include<stdio.h> 6 #include"graphics.h" 7 #include<stdlib.h> 8 9 //函数声

codevs 2039 骑马修栅栏 USACO x

我承认我是转的@自为风月马前卒 嘿~ 题目描述 Description Farmer John每年有很多栅栏要修理.他总是骑着马穿过每一个栅栏并修复它破损的地方. John是一个与其他农民一样懒的人.他讨厌骑马,因此从来不两次经过一个栅栏.你必须编一个程序,读入栅栏网络的描述,并计算出一条修栅栏的路径,使每个栅栏都恰好被经过一次.John能从任何一个顶点(即两个栅栏的交点)开始骑马,在任意一个顶点结束. 每一个栅栏连接两个顶点,顶点用1到500标号(虽然有的农场并没有500个顶点).一个顶点上可

codevs 水过 骑马修栅栏

[问题描述] 农民John每年有很多栅栏要修理.他总是骑着马穿过每一个栅栏并修复它破损的地方. John是一个与其他农民一样懒的人.他讨厌骑马,因此从来不两次经过一个一个栅栏.你必须编一个程序,读入栅栏网络的描述,并计算出一条修栅栏的路径,使每个栅栏都恰好被经过一次.John能从任何一个顶点(即两个栅栏的交点)开始骑马,在任意一个顶点结束. 每一个栅栏连接两个顶点,顶点用1到500标号(虽然有的农场并没有500个顶点).一个顶点上可连接任意多(>=1)个栅栏.所有栅栏都是连通的(也就是你可以从任

栅栏——CyclicBarrier

栅栏CyclicBarrier和闭锁CountDownLatch类似,可以说它们都是用来计数,都能阻塞一组线程知道某个事件发生.不同的是闭锁用于等待事件,而栅栏用于等待其他线程. 在前一篇<CountDownLatch--闭锁的实现之一>中提到,在CountDownLatch里有一个计数器,一个线程完成一个任务后调用countdown方法使其计数器-1,等待线程则在执行方法前调用await方法,当CountDownLatch计数器减至0时将会放行的阻塞等待线程.那么CyclicBarrier所

洛谷P2731 骑马修栅栏 Riding the Fences

P2731 骑马修栅栏 Riding the Fences• o 119通过o 468提交• 题目提供者该用户不存在• 标签USACO• 难度普及+/提高 提交 讨论 题解 最新讨论 • 数据有问题题目背景Farmer John每年有很多栅栏要修理.他总是骑着马穿过每一个栅栏并修复它破损的地方.题目描述John是一个与其他农民一样懒的人.他讨厌骑马,因此从来不两次经过一个栅栏.你必须编一个程序,读入栅栏网络的描述,并计算出一条修栅栏的路径,使每个栅栏都恰好被经过一次.John能从任何一个顶点(即