恩,看了他人的题解,才勉强理解,希望能有所收获。
思路:这道题要使用搜索+二分,可能有部分像我一样的会觉得这道题难道不是排序后一个一个去判断就可以了吗?那么这里可以举个例子!
这幅图你就会发现,如果按一个个取,就会发现只能得到2和3,但实际上6会切成2和4,3切成3,三种都会得到。
所以不是能取就切的,2应该在6那里取。
我们用回溯的方法,去看看这块木板应从哪里切,这样只要有一种情况下,能切出来就成立,二分枚举我们切前几块。
好啦,具体思路就从程序里看吧!
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; int ans,w,a[2000]={0},c[2000]={0},b[2000]={0},m,n,tot,mid,lef,righ; bool check(int x,int last)//x指现在还需多少木板,last用于第二个剪枝,下面再讲。 { int i; bool fg; if (w>tot-c[mid]) return false;//剪枝一:如果多出的废料木板的总量大于理想中提供的木板减去现在枚举需要的木板(即理想中能够多出的木板)
,那么接下来肯定没法提供所需木料了,所以返回false。w在下面有写。 if (x==0) return true;// for (i=last;i<=m;++i)//枚举能提供的木板 if (a[i]>=b[x])//如果这块提供的木板能切出所需的木板,那么就试着切! { a[i]-=b[x];//切完后剩下的多少木料。 if (a[i]<b[1]) w+=a[i];//如果剩下的长度小于最小的所需木板,那么它肯定不能再被利用,算作废料。 w记录废料的总长度。 if (b[x-1]==b[x]) fg=check(x-1,i); // 如果下块需要的木板,和现在这块切出的木板一样,那么它提供的木板至少要大于等于目前提供的木板,last即用于记录这个位置,如果两块木板长度一样
,last就为i,即下次只能从i以后能提供的木板开始枚举。 else fg=check(x-1,1); //否则就从第一块枚举。 if (a[i]<b[1]) w-=a[i]; a[i]+=b[x];//回溯,避免对下次造成影响。 if (fg==true) return true;//如果剩下的都切的出来,那么就成立啦! } return false; } int main() { cin>>m; tot=0; for (int i=1;i<=m;++i) { cin>>a[i]; tot+=a[i]; } cin>>n; c[0]=0; for (int i=1;i<=n;++i) cin>>b[i]; w=0; sort(a+1,a+m+1); sort(b+1,b+n+1); for (int i=1;i<=n;++i) c[i]=c[i-1]+b[i];//为了以后的剪枝,后面会写。 while (c[n]>tot) n--; lef=0; righ=n; ans=0; while (lef<=righ)//二分枚举是否能切出前m块。 { w=0; mid=(lef+righ)/2; if (check(mid,1)==true) { ans=mid; lef=mid+1; } else righ=mid-1; } cout<<ans<<endl; return 0; }
很抱歉的几乎抄袭了一位同学的代码,这是他博客原址,在此道歉。
www.cnblogs.com/lztlztlzt/p/6196223.html