题目:洛谷P2672、codevs5126、Vijos P1977
题目大意:有个推销员要去推销,要你求他推销1~n户人家分别最多花多少“疲劳值”。具体见题目。
解题思路:如果用$O(n^2)$做的话,可以得到60分。如何得到满分呢?
贪心,先选择$s[i]*2+a[i]$值最大的一个点,并将这个点记为now,用堆保存now左边的点的疲劳值。每次选择点时,取出左边疲劳值最大的点,枚举now右边的点j,找出右边疲劳值最大的点(这里要加上2*(now到j的距离)),两个比较,取大值。如果左边的大,则直接弹出堆,否则将now更新,把old_now+1~new_now-1之间的点的$a[i]$扔进堆里即可。时间复杂度,最好情况下是$O(n log n)$,最坏情况下是$O(n^2)$,数据比较平均,所以能得满分。
此题我在取出堆元素时没有判断是否为空,然而在上面的OJ中提交都没有出错,说明最坏情况并没有出现在数据中。
C++ Code:
#include<cstdio> #include<ext/pb_ds/priority_queue.hpp> __gnu_pbds::priority_queue<int>ld; int n; int a[100005],s[100005]; int main(){ scanf("%d",&n); int mxi=0,mx=0; for(int i=1;i<=n;++i)scanf("%d",&s[i]); for(int i=1;i<=n;++i)scanf("%d",&a[i]); for(int i=1;i<=n;++i) if(s[i]*2+a[i]>mx){ mx=s[i]*2+a[i]; mxi=i; } printf("%d ",mx); for(int i=1;i<mxi;++i)ld.push(a[i]); int now=mxi; for(int i=2;i<=n;++i){ int lmax=0,rmax=0,ri=0; if(!ld.empty())lmax=ld.top(); for(int j=now+1;j<=n;++j) if(s[j]*2-s[now]*2+a[j]>rmax){ rmax=s[j]*2-s[now]*2+a[j]; ri=j; } if(lmax>rmax){ mx+=lmax; ld.pop(); }else{ mx+=rmax; for(int j=now+1;j<ri;++j) ld.push(a[j]); now=ri; } printf("%d ",mx); } return 0; }