搜索,剪枝
首先可以二分答案迭代加深,假设要买 $p$ 台
那么肯定卖价格最小的 $p$ 台
再来个 $A*$ ,设搜到当前情况时,有 $waste$ 的钱一定要被浪费(其实就是某些学校剩下的钱连最便宜的都买不起)
设最便宜的 $p$ 台电脑总价值为 $sum$ ,所有学校的总钱数为 $S$,那么我们最多浪费 $S-tot$,如果 $waste>S-tot$ 就直接返回
但是显然还是不够
看看数据发现相同价格的电脑和相同初始钱数的学校数量很多
不妨使搜索时保证,对于相同价格的电脑,购买的学校的初始钱数单调不增,显然这样不会影响搜索时的正确性
然后就可以跑过了
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; typedef long long ll; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=5e5+7; int n,m,mon[N],cst[N],ans; int S,sum[N],Mx,Las[N]; bool GG; void dfs(int pos,int waste) { if(waste>Mx) return; if(!pos) { GG=1; return; } int R=Las[cst[pos]]; for(int i=R;i;i--) { if(cst[pos]>mon[i]) continue; mon[i]-=cst[pos]; Las[cst[pos]]=i; dfs(pos-1,waste+ (mon[i]<cst[1])*mon[i] ); mon[i]+=cst[pos]; if(GG) return;//记得先还原mon再返回 } Las[cst[pos]]=R; } bool check(int p) { if(sum[p]>S||cst[p]>mon[m]) return 0; GG=0; Mx=S-sum[p]; for(int i=1;i<=n;i++) Las[cst[i]]=m;//每次都要初始化Las dfs(p,0); return GG; } int main() { m=read(); for(int i=1;i<=m;i++) mon[i]=read(),S+=mon[i]; n=read(); for(int i=1;i<=n;i++) cst[i]=read(); sort(mon+1,mon+m+1); sort(cst+1,cst+n+1); for(int i=1;i<=n;i++) sum[i]=sum[i-1]+cst[i]; int L=0,R=n; while(L<=R) { int mid=L+R>>1; if(check(mid)) L=mid+1,ans=mid; else R=mid-1; } printf("%d ",ans); return 0; }