【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 }
View Code
原文地址:https://www.cnblogs.com/JSL2018/p/6526190.html