Description
农夫约翰打算建立一个栅栏将他的牧场给围起来,因此他需要一些特定规格的木材。于是农夫约翰到木材店购
买木材。可是木材店老板说他这里只剩下少部分大规格的木板了。不过约翰可以购买这些木板,然后切割成他所需
要的规格。而且约翰有一把神奇的锯子,用它来锯木板,不会产生任何损失,也就是说长度为10的木板可以切成长
度为8和2的两个木板。你的任务:给你约翰所需要的木板的规格,还有木材店老板能够给出的木材的规格,求约翰
最多能够得到多少他所需要的木板。
Input
第一行为整数m(m<= 50)表示木材店老板可以提供多少块木材给约翰。紧跟着m行为老板提供的每一块木板的长
度。接下来一行(即第m+2行)为整数n(n <= 1000),表示约翰需要多少木材。接下来n行表示他所需要的每一块木板
的长度。木材的规格小于32767。(对于店老板提供的和约翰需要的每块木板,你只能使用一次)。
Output
只有一行,为约翰最多能够得到的符合条件的木板的个数。
嗯……今天的考试题……出题人把数据一加强,卡掉无数贪心……
看了看这道题,发现分类竟然是基础搜索题……好吧,看来我离noip还有段距离……这种题我做不出……
去网上翻了翻题解,发现都是二分答案,用搜索来检验。这样复杂度虽然还是不对,但显然已经比普通的搜索好到不知道哪里去了。我以前打的二分答案都是用贪心或者dp来检验,今天还是第一次用搜索来检验。新姿势get
于是,我们每次二分一个答案x,然后显然是尽量选小的木板,于是排序后枚举每个木板去组成什么木材。
当然,就算这样比朴素搜索要好很多,但是不加剪枝的话显然还是过不了的。于是,仔细思考之后,可以有以下几个优化:
1.当需要的木板不足最小的可以提供的木板时,显然已经没有用了,于是我们可以直接把它丢弃,并记录一下丢弃的木板总长度。当丢弃的木板和前x块木板的长度总和超过所有提供的木板的和时,可以剪掉。
2.当两个木板长度相同时,那么交换他们组成什么木材对答案没有影响。于是我们就可以强制前面的木板去组成编号大于等于后一个的木材。
加了这两个剪枝就可以过了。当然,由于原题数据水,有些贪心也是可以过的。
下面贴代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) #define N 2010 #define INF 2147483647 using namespace std; typedef long long llg; int n,m,a[N],b[N],w[N]; int tol,diu,l,r,mid,xu[N]; bool ww; int getint(){ int w=0;bool q=0; char c=getchar(); while((c>'9'||c<'0')&&c!='-') c=getchar(); if(c=='-') c=getchar(),q=1; while(c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w; } void dfs(int u){ if(!u) ww=1; if(ww || diu+w[mid]>tol) return; int l=1; if(b[u]==b[u+1] && u!=mid) l=xu[u+1]; for(int i=l;i<=n;i++) if(a[i]>=b[u]){ xu[u]=i; a[i]-=b[u]; if(a[i]<b[1]) diu+=a[i]; dfs(u-1); if(a[i]<b[1]) diu-=a[i]; a[i]+=b[u]; } } int main(){ File("a"); n=getint(); for(int i=1;i<=n;i++) tol+=(a[i]=getint()); sort(a+1,a+n+1); m=getint(); for(int i=1;i<=m;i++) b[i]=getint(); sort(b+1,b+m+1); for(int i=1;i<=m;i++) w[i]=w[i-1]+b[i]; l=0,r=m+1; while(l!=r){ mid=l+r>>1; ww=0; dfs(mid); if(ww) l=mid+1; else r=mid; } printf("%d",l-1); }