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 }