bzoj1082: [SCOI2005]栅栏(二分答案搜索判断)

1082: [SCOI2005]栅栏

题目:传送门 

题解:

   是不是一开始在想DP?本蒟蒻也是qwq,结果很nice的错了ORZ

   正解:二分+搜索

   我们可以先把两种木材都进行排序,那么如果需要的最大木材比可提供的最大木材还要大的话,那么可以直接舍弃这种需要的木材。

   然后就可以进入二分,如果当前可以做贡献的提供木材加起来都没有前mid块需要木材大的话,很明显当前mid不ok

   返回判断值再记录答案就好了,注意一些小细节的优化

代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<cmath>
 5 #include<algorithm>
 6 typedef long long LL;
 7 using namespace std;
 8 bool flag;
 9 int n,m,mid;
10 int a[55],b[1005],bl[1005];
11 LL sa;
12 int sb[1005];
13 void dfs(int ak,int bk,int w)
14 {
15     if(bk==0)flag=1;
16     while(ak<=n && a[ak]<b[1]){w+=a[ak];ak++;}//当前木材比最小的需求值还要小,跳下一个木材 
17     if(flag || ak>n)return;//如果已经合法或者没有符合要求的木材了就退出(因为已经排序过了啊) 
18     if(w+sb[mid]>sa)return;//如果有贡献的提供木材加起来都没有前mid块需要木材大,肯定不ok
19     int t=ak,t1=ak,t2=bk,t3=w;
20     if(b[bk]==b[bk+1] && bk!=mid)t=bl[bk+1];//小剪枝,如果我当前需要的木块和上一块一样,直接跳到上一次使用过的木材 
21     for(int i=t;i<=n;i++)
22         if(a[i]>=b[bk])
23         {
24             bl[bk]=i;a[i]-=b[bk];
25             bk--;
26             dfs(ak,bk,w);
27             ak=t1;bk=t2;w=t3;a[i]+=b[t2];
28         }
29 }
30 int main()
31 {
32     scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&a[i]);
33     scanf("%d",&m);for(int i=1;i<=m;i++)scanf("%d",&b[i]);
34     sort(a+1,a+n+1);sort(b+1,b+m+1);
35     while(b[m]>a[n])m--;//如果需要的最大木材比可提供的最大木材还要大,舍弃,直接m-- 
36     int tot=0;
37     for(int i=1;i<=n;i++)if(a[i]>b[1])a[++tot]=a[i];
38     n=tot;
39     for(int i=1;i<=n;i++)sa+=a[i];
40     for(int i=1;i<=m;i++)sb[i]=sb[i-1]+b[i];
41     int l=1,r=m,ans=0;
42     while(l<=r)
43     {
44         mid=(l+r)/2;
45         flag=0;
46         dfs(1,mid,0);
47         if(flag!=0)ans=mid,l=mid+1;
48         else r=mid-1;
49     }
50     printf("%d\n",ans);
51     return 0;
52 }

 

posted @ 2018-03-01 13:16  CHerish_OI  阅读(242)  评论(0编辑  收藏  举报