BZOJ1082 四川省选 栅栏 【例题详解】

题目描述

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

你的任务:给你约翰所需要的木板的规格,还有木材店老板能够给出的木材的规格,求约翰最多能够得到多少他所需要的木板。

输入输出格式

输入格式:

 

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

 

输出格式:

 

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

 

初看这道题,当然要用深搜解决。试想一下,如何让符合条件的木板数量最多,首先,要让需要的木板长度尽可能短,其次还得让浪费的尽可能少。所以我们决定从小到大把需要的木板长度排序。然后从短的板子开始切,切到不能切了换下一块板子;

但对于这道题,纯纯的暴搜绝对是过不了的,需要加一定的剪枝,所以不难想到以下几点

1.二分答案:如果我们要确定符合条件的木板个数,一定会需要枚举可能的木板长度,但这个长度是符合单调性的,(如果一个人木板个数是符合条件的最大个数的话),那么比他再大的个数,一定无法搜出结果,所以可以用到二分答案,然后深搜判断。

2.既然是切割,那么一定会有浪费,浪费的多少对结果是有直接影响的,如果浪费的加上目前需要的板子总长已经大于了所有提供的板子的长,那这一条路线可以减掉;

3.如果两种需要的木板长度相等,那我搜索的时候之前那些根本切不出来的提供的板子就可以不搜了,上一层记录一下,这次直接那块已提供板子开始搜索;

说到这里这道题大概思路就出来了,剩下的就是代码实现;

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<string>
 4 #include<cstring>
 5 #include<cmath>
 6 #include<cstdlib>
 7 #include<algorithm>
 8 #include<stack>
 9 using namespace std;
10 const int MAXN=2005;
11 int n,m,p,tot,ans,need[MAXN],provide[MAXN],sum[MAXN];
12 int waste;
13 bool dfs(int k,int sign)
14 {
15     if(k==0)return 1;
16     if(waste+sum[p]>tot) return 0;
17     for(int i=sign;i<=n;i++){
18         if(provide[i]>=need[k]){
19             provide[i]-=need[k];
20             if(provide[i]<need[1]) waste+=provide[i];
21             if(need[k]==need[k-1]){
22                 if(dfs(k-1,i)){
23                     if(provide[i]<need[1]) {
24                         waste-=provide[i];
25                     }
26                     provide[i]+=need[k];return 1;//回溯 
27                 }
28             }
29             else{
30                 if(dfs(k-1,1)){
31                     if(provide[i]<need[1]) {
32                         waste-=provide[i];
33                     }
34                     provide[i]+=need[k];return 1;
35                 }
36             } 
37             if(provide[i]<need[1]) waste-=provide[i];
38             provide[i]+=need[k];
39         }
40     }
41     return false ;
42 }
43 bool pd(int x)
44 {
45     p=x;waste=0;if(dfs(p,1)) return 1;
46     return 0;
47 }
48 int main()
49 {
50     scanf("%d",&n);tot=0;
51     for(int i=1;i<=n;i++){
52         scanf("%d",&provide[i]);
53         tot+=provide[i];
54     }
55     scanf("%d",&m);
56     int l=0,r=0;
57     for(int i=1;i<=m;i++){
58         scanf("%d",&need[i]);
59     }
60     sort(provide+1,provide+1+n);sort(need+1,need+1+m);
61     for(int i=1;i<=m;i++){
62         sum[i]=sum[i-1]+need[i];
63         if(sum[i]>tot) r=min(r,sum[i]);if(provide[i]>need[i]) l++;
64     }
65     while(l<r-1){
66         int mid=(l+r)>>1;
67         if(pd(mid)) l=mid+1;
68         else r=mid-1;
69     }
70     if(l==r) printf("%d\n",l);
71     else if(pd(r)) printf("%d\n",r);
72     else printf("%d\n",l);
73     return 0;
74 }
View Code

 

转载于:https://www.cnblogs.com/Alan-Luo/articles/8721306.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值