切蛋糕
题目描述
Facer今天买了n块蛋糕,不料被信息组中球球等好吃懒做的家伙发现了,没办法,只好浪费一点来填他们的嘴巴。他答应给每个人留一口,然后量了量每个人口的大小。Facer有把刀,可以切蛋糕,但他不能把两块蛋糕拼起来,但是他又不会给任何人两块蛋糕。现在问你,facer怎样切蛋糕,才能满足最多的人。(facer的刀很强,切的时候不会浪费蛋糕)。
输入输出格式
输入格式:
第一行n,facer有n个蛋糕。接下来n行,每行表示一个蛋糕的大小。再一行一个数m,为信息组的人数,然后m行,每行一个数,为一个人嘴的大小。(1<=n<=50, 1<=m<=1024)
输出格式:
一行,facer最多可以填多少张嘴巴。
输入输出样例
输出样例#1:
7
分析:
一道极其恶心的搜索题。
首先我们不难想到,一块蛋糕可以给一个嘴大的人或者给几个嘴小的人,那显然是给嘴小的人能得到局部最优。但是局部最优并不一定能得到全局最优,所以我们要搜索啊(废话。。。先对所有人按嘴的大小排序,如果蛋糕总和也不能满足嘴最大的人,那么就可以直接把他踢出去了(23333,然后二分能满足的人数,深搜检验即可。
当然这样做复杂度肯定还是承受不了,还需要剪枝。因为有的蛋糕在给一些人吃了之后还有剩余,但是如果剩余部分连嘴最小的人也满足不了那就只能丢弃,我们就可以在dfs的时候记录一个waste值,表示不能在利用的蛋糕的大小,如果蛋糕总体积减去waste值小于剩余人数的需求值,就可以直接退出。加上这个优化以后复杂度就非常优秀了。
Code:
//It is made by HolseLee on 7th Aug 2018 //Luogu.org P1528 #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<iostream> #include<iomanip> #include<algorithm> using namespace std; const int N=61; const int M=1055; int n,m,tot,waste,mou[M],all[M],c[N],t[N],ans,l,r,mid; inline int read() { char ch=getchar();int num=0;bool flag=false; while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();} while(ch>='0'&&ch<='9'){num=num*10+ch-'0';ch=getchar();} return flag?-num:num; } inline bool dfs(int num,int sta) { if(!num)return 1; if(tot-waste<all[mid])return 0; for(int i=sta;i<=n;++i) if(t[i]>=mou[num]){ t[i]-=mou[num]; if(t[i]<mou[1])waste+=t[i]; if(mou[num]==mou[num-1]){ if(dfs(num-1,i))return 1;} else { if(dfs(num-1,1))return 1;} if(t[i]<mou[1])waste-=t[i]; t[i]+=mou[num]; } return 0; } int main() { n=read(); for(int i=1;i<=n;++i){ c[i]=read(); tot+=c[i]; } m=read(); for(int i=1;i<=m;++i)mou[i]=read(); sort(mou+1,mou+m+1); while(mou[m]>tot)m--; for(int i=1;i<=m;++i) all[i]=all[i-1]+mou[i]; l=0,r=m; while(l<=r){ waste=0; mid=(l+r)>>1; for(int i=1;i<=n;++i)t[i]=c[i]; if(dfs(mid,1))l=mid+1,ans=mid; else r=mid-1; } printf("%d ",ans); return 0; }