BZOJ【1082】栅栏

1082: [SCOI2005]栅栏

Time Limit: 10 Sec Memory Limit: 162 MB
Description

农夫约翰打算建立一个栅栏将他的牧场给围起来,因此他需要一些特定规格的木材。于是农夫约翰到木材店购
买木材。可是木材店老板说他这里只剩下少部分大规格的木板了。不过约翰可以购买这些木板,然后切割成他所需
要的规格。而且约翰有一把神奇的锯子,用它来锯木板,不会产生任何损失,也就是说长度为10的木板可以切成长
度为8和2的两个木板。你的任务:给你约翰所需要的木板的规格,还有木材店老板能够给出的木材的规格,求约翰
最多能够得到多少他所需要的木板。

Input

第一行为整数m(m<= 50)表示木材店老板可以提供多少块木材给约翰。紧跟着m行为老板提供的每一块木板的长
度。接下来一行(即第m+2行)为整数n(n <= 1000),表示约翰需要多少木材。接下来n行表示他所需要的每一块木板
的长度。木材的规格小于32767。(对于店老板提供的和约翰需要的每块木板,你只能使用一次)。

Output

只有一行,为约翰最多能够得到的符合条件的木板的个数。

Sample Input

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

Sample Output

7

这道题可以用搜索加2分来做。。。
代码最主要的优化就是:如果浪费的木材+需要的材料比总值还要多,就是最小的木块都比它大,那么他当前就已经分不出任何木块了。
不懂的就看我的代码注释咯!!WAW

#include<cstdio>     
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<set>
#include<map>
#include<sstream>
#include<iostream>
#include<algorithm>
//一堆猥琐头文件 
using namespace std;
int a[110],b[1100],s[1100];
int n,m,ans,mid,kk;
int cmp(const void *xx,const void *yy)    //CMP,不要告诉我你不会 
{
    int n1=*(int *)xx;
    int n2=*(int *)yy;
    if(n1>n2) return 1;
    if(n1<n2) return -1;
    return 0;
}
bool dfs(int k,int now)
{
    if(k==0) return true;     //如果k=0就说明这个木头可以得到 
    if(s[mid]+kk>ans) return false;   //如果这个木头加上那些零碎的木头比老板的总和大的话,就得不到这块木头 
    bool bk;
    for(int i=now;i<=m;i++)   //从老板剩下的木头开始for 
    {
        if(a[i]>=b[k])    //如果老板这块木头可以切出b[k]这块木头 
        {
            a[i]-=b[k];   //就切掉b[k]这块木头 
            if(a[i]<b[1]) kk+=a[i];   //如果老板的木头比约翰最小的一块小,就累加 
            if(b[k-1]==b[k]) bk=dfs(k-1,i);  //如果前一块木头和现在的规格一样,就从now询问b[k-1] 
            else bk=dfs(k-1,1);  //否则就从1开始询问b[k-1] 
            if(a[i]<b[1]) kk-=a[i];    //如果现在这块木头还比最小的小,就从总和中减去它 
            a[i]+=b[k];     //老板的木头就加回b[k]的规格 
            if(bk==true) return true;   //这块木头可以获得 
        }
    }
    return false;   //这块木头不能获得 
}
int main()
{
//  freopen("fency.in","r",stdin);     这是本机测试的语句 
//  freopen("fency.out","w",stdout);
    ans=0;
    scanf("%d",&m);
    for(int i=1;i<=m;i++)          //先将老板所有木头的规格相加 
    {
        scanf("%d",&a[i]);
        ans+=a[i];
    }
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&b[i]);
    qsort(a+1,m,sizeof(int),cmp);      //快排 
    qsort(b+1,n,sizeof(int),cmp);
    s[0]=0; for(int i=1;i<=n;i++) s[i]=s[i-1]+b[i];     //计算约翰木头尺寸的和 
    while(s[n]>ans) n--;     //如果约翰木头尺寸和最大的大于老板所有木头的尺寸和,就将这个值删除 
    int l=1,r=n,answer=0;              
    kk=0;
    while(l<=r)       //二分查找 
    {
        mid=(l+r)/2;
        if(dfs(mid,1)) 
        {
            l=mid+1; answer=mid;    //记录结果 
        }
        else r=mid-1;
    } 
    printf("%d\n",answer);
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值