【BZOJ1082】【SCOI2005】栅栏
暴力出奇迹……
原题:
农夫约翰打算建立一个栅栏将他的牧场给围起来,因此他需要一些特定规格的木材。于是农夫约翰到木材店购
买木材。可是木材店老板说他这里只剩下少部分大规格的木板了。不过约翰可以购买这些木板,然后切割成他所需
要的规格。而且约翰有一把神奇的锯子,用它来锯木板,不会产生任何损失,也就是说长度为10的木板可以切成长
度为8和2的两个木板。你的任务:给你约翰所需要的木板的规格,还有木材店老板能够给出的木材的规格,求约翰
最多能够得到多少他所需要的木板。
m<= 50,n <= 1000
第一眼就脑补出暴力做法二分+dfs,然而想了想觉得dfs的复杂度非常不大丈夫啊
然后搜题解,就是dfs……
首先需要的木条递增排序然后降序dfs,对于每次搜到的木条遍历所有大木条
两个剪枝
搞个前缀和s[i]表示从第1到第i个需要的木条的长度和,用tot表示提供的木条的总长度,如果某个大木条剩下的部分比需要的最小的木条还短,这个大木条就相当于浪费掉了,加到一个waste里面,如果某层dfs中dfs到的木条的前缀和+waste比tot大,就直接return
如果当前木条和下一层要dfs到的木条长度一样,下一层搜的时候就直接从当前木条使用的大木条往后遍历(如果不一样就只能从1开始遍历了
然后这样剪枝直接把复杂度50^1000的dfs_1s跑过了……
暴力出奇迹……
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 int rd(){int z=0,mk=1; char ch=getchar(); 7 while(ch<'0'||ch>'9'){if(ch=='-')mk=-1; ch=getchar();} 8 while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0'; ch=getchar();} 9 return z*mk; 10 } 11 int n,m; int a[1100],b[1100]; 12 int wst=0,s[1100],tt=0,p; 13 bool dfs(int x,int y){ 14 if(!x) return true; 15 if(wst+s[p]>tt) return false; 16 bool flg; 17 for(int i=y;i<=n;++i)if(a[i]>=b[x]){ 18 a[i]-=b[x]; 19 if(a[i]<b[1]) wst+=a[i]; 20 flg=dfs(x-1,(b[x]==b[x-1] ? i : 1)); 21 if(a[i]<b[1]) wst-=a[i]; 22 a[i]+=b[x]; 23 if(flg) return true; 24 } 25 return flg; 26 } 27 int bnrsch(int l,int r){ 28 int md; 29 while(l+1<r){ md=(l+r)>>1; (dfs(p=md,1) ? l : r)=md;} 30 return dfs(r,1) ? r : l; 31 } 32 int main(){//freopen("ddd.in","r",stdin); 33 cin>>n; 34 for(int i=1;i<=n;++i) tt+=(a[i]=rd()); 35 sort(a+1,a+n+1); 36 cin>>m; 37 int mn=0,mx=m; 38 for(int i=1;i<=m;++i) b[i]=rd(); 39 sort(b+1,b+m+1); 40 for(int i=1;i<=m;++i){ 41 s[i]=s[i-1]+b[i]; 42 mn+=(a[i]>b[i]); if(s[i]>tt) mx=min(mx,i-1); 43 } 44 cout<<bnrsch(mn,mx)<<endl; 45 return 0; 46 }