该题第一思路是想去模拟题目中所描述的过程
这里我选择从大到小遍历可能凑出的牌套数,计算凑出它需要补的牌数以及判断是否会超出能补的牌数
#include<iostream> #include<climits> #include<vector> #include<algorithm> #define int long long using namespace std; const int MAX=2e5+10; int a[MAX]; int b[MAX]; int n,m,ans=0,num=0; vector<int>v; signed main(){ cin>>n>>m; for(int i=1;i<=n;i++){ cin>>a[i]; v.push_back(a[i]); } for(int i=1;i<=n;i++){ cin>>b[i]; } sort(v.begin(),v.end());//排序 for(int i=n-1;i>=0;i--){//尝试将每一种卡牌都变成v[i]张 int ele=v[i]; int sign=0; int sum=0; for(int j=1;j<=n;j++){ int need=v[i]-a[j];//需要补上 if(need>0) sum+=need; if(b[j]-need<0){ sign=1; break;//不能补 } } if(sum>m||sign){ continue; }else{ cout<<v[i]<<endl; break; } } return 0; }
由于该算法效率低下并不能通过所有样例
但是仔细理解题意,我们可以发现该方法可以使用二分进行优化
#include<iostream> #include<algorithm> #define int long long using namespace std; const int MAX=2e5+10; int n,m,l,r; int a[MAX]; int b[MAX]; bool check(int x){ int s = 0; for (int i = 0; i < n; i++) { if (x - a[i] <= b[i]) { s += max(x - a[i], 0ll);//统计组成x套牌需要补s张牌 } else return false;//超出了b[i] } if (s <= m) return true;//没有越界 return false; } signed main(){ cin>>n>>m; for(int i=0;i<n;i++){ cin>>a[i]; l = min(l, a[i]); } for(int i=0;i<n;i++){ cin>>b[i]; r=max(r,a[i]+b[i]); } int ans; while(l<=r){ int mid=(l+r)/2;//二分组成的牌套数 if(check(mid)){ ans=mid; l=mid+1; }else{ r=mid-1; } } cout<<ans<<endl; return 0; }
第二思路是使用“贪心”的思想进行解答
首先要理解一个东西,卡牌套数等于最少的卡牌牌数。因为一套卡牌需要所有卡牌各一张,所以对于最少的卡牌,它如果只有 x 张,则只能有 x 套卡牌含有最少的卡牌。再多的其他卡牌就没用了,所以卡牌套数等于最少的卡牌牌数。
那具体怎么贪心呢?使卡牌套数最多。由于卡牌套数等于最少的卡牌牌数,只需要尽量让最终各种卡牌数量接近即可。那就优先画数量少的卡牌,直到空卡牌用完。
每次 O(n) 选择最小的卡牌,复杂度会很高。由于这个贪心策略是要连续选择最小的,所以可以通过排序来降低复杂度。
最后还有一个问题:可画的卡牌数有限制。还是根据卡牌套数等于最少的卡牌牌数,在空卡牌够用的情况下,最终的卡牌套数取决于初始卡牌数与可画卡牌数的和最少的卡牌。所以可以判断目前求得的卡牌数是否大于每张卡牌初始卡牌数与可画卡牌数的和的最小值来解决这个问题。若小于,继续贪心。若大于,则证明空卡牌够用,但受可画的卡牌数的限制,卡牌套数只能为每张卡牌初始卡牌数与可画卡牌数的和的最小值。(好吧这一段有点难理解,可以结合代码里的注释理解)
#include<iostream> #include<algorithm> #include<climits> #define int long long const int MAX=2e6; using namespace std; int n,m; int a[MAX]; int b; signed main(){ int consume=0,minn=INT_MAX; cin>>n>>m; for(int i=0;i<n;i++){ cin>>a[i]; } for(int i=0;i<n;i++){ cin>>b; if(a[i]+b<minn){ minn=a[i]+b;//得到最大套牌数 } } sort(a,a+n);//其实无所谓对应下标 int ans=a[0];//初始值为最小牌数 for(int i=0;i<n;i++){ if(i==n-1&&consume<m){ ans+=(m-consume)/n;//剩余卡牌平均分 if(ans>minn)ans=minn; break; } if(a[i]<a[i+1]){//前面卡牌不够 //这里不能且无需去修改数组中的内容(会降低代码效率) consume+=(i+1)*(a[i+1]-a[i]);//消耗的卡牌 ans+=(a[i+1]-a[i]); } if(consume>m)//不够牌补了 { consume-=(i+1)*(a[i+1]-a[i]); ans-=(a[i+1]-a[i]); ans+=(m-consume)/(i+1);//平均分 break; } if(ans>minn){ ans=minn; break; } } cout<<ans<<endl; return 0; }