洛谷P2672 推销员(贪心)

题意

一条直线上有 N ( N ≤ 1 0 5 ) N(N\leq10^5) N(N105)个点,每个点有位置 p i p_i pi和权值 a i a_i ai。求从 0 0 0点开始,走过 k = 1 , 2 , … N k=1,2,\dots N k=1,2,N个点并回到原点,且不走多余路程所能得到的最大权值是多少。也就是说,一次行走集合为 S S S的点的权值计算方式为 ( ∑ i ∈ S a i ) + max ⁡ i ∈ S 2 p i (\sum_{i \in S}a_i) +\max_{i\in S} 2p_i (iSai)+maxiS2pi

解题思路

由于 N N N很大,所以DP什么的估计是行不通的,做题的时候想了很久也没想到有效的贪心算法。看来普及组的贪心题也是有点神仙的。
先从 k = 1 k=1 k=1开始想,这个时候答案显然是 2 p i + a i 2p_i+a_i 2pi+ai最大的那个。当 k = 2 k=2 k=2的时候,那么有两种情况。如果保留之前的那个点,那么增量要么是一个很大的 2 p j − 2 p i 2p_j-2p_i 2pj2pi,要么是一个 a j a_j aj而不可能同时增加。
否则,假设有另外两个点 j , k j,k j,k,那么总权值为 a j + 2 p j + a k + 2 p j − m i n ( 2 p j , 2 p k ) a_j+2p_j+a_k+2p_j-min(2p_j,2p_k) aj+2pj+ak+2pjmin(2pj,2pk)。如果让 p k > p j p_k>p_j pk>pj,那么原式子就是 a j + a k + 2 p k a_j+a_k+2p_k aj+ak+2pk
那么考虑这两种情况下的最优解,情况一为 a i + a j + 2 p j a_i+a_j+2p_j ai+aj+2pj或者 a i + a j + 2 p i a_i+a_j+2p_i ai+aj+2pi,情况二为 a j + a k + 2 p k a_j+a_k+2p_k aj+ak+2pk。我们可以发现式子很相近,但是由于最大值只能是 a i + 2 p i a_i+2p_i ai+2pi,所以情况二可以和情况1合并。并且 a j + 2 p j ≤ a i + 2 p i a_j+2p_j\leq a_i + 2p_i aj+2pjai+2pi,所以 a a a的大小成了至关重要的因素。
我们可以把情况推广到 k > 2 k>2 k>2的情形,对于某个 k k k,一定有权值为 ∑ i = 1 k − 1 a i + a j + 2 p j \sum_{i=1}^{k-1}a_i+a_j+2p_j i=1k1ai+aj+2pj,此时要么 a j + 2 p j a_j+2p_j aj+2pj占据绝对优势,要么 ∑ i = 1 n − 1 a i \sum_{i=1}^{n-1}a_i i=1n1ai占据绝对优势才能胜出。简单来说,就是要么选择 k k k a a a值最大的点,要么选择 k − 1 k-1 k1 a a a值最大的点和一个不在 k − 1 k-1 k1个点中的 a j + 2 p j a_j + 2p_j aj+2pj最大值,那么两种情况取最大值就是当前最优决策。
具体实现可以先把点按照 a a a值从大到小排好序并记录前缀和 p r e f pref pref,然后维护一个从 N N N 1 1 1的逆序最大 a j + 2 p j a_j+2p_j aj+2pj叫做 s u f suf suf。此外,我们还需要预先计算出前 i i i个点的最远距离 c u r P curP curP。这样我们的决策就是 max ⁡ { p r e f [ i − 1 ] + s u f [ i ] , p r e f [ i ] + c u r P [ i ] } \max\{pref[i-1]+suf[i], pref[i]+curP[i]\} max{pref[i1]+suf[i],pref[i]+curP[i]}。由于上面的推导可知,这样的贪心策略是正确的。
(累死了)

时间复杂度

O ( N log ⁡ N ) O(N\log{N}) O(NlogN)

代码

#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
#include<queue>

using namespace std;
typedef long long ll;
const int MAXN = 100005;
const int INF2 = 0x3f3f3f3f;
int n, m, k;
struct Object{
	int p, a;
	bool operator<(const Object& b) const {
		return a > b.a;
	}
};
int pref[MAXN], maxp[MAXN], maxv[MAXN];
Object objects[MAXN];
int main(){
	scanf("%d", &n);
	for(int i = 1; i <= n; i++){
		scanf("%d", &objects[i].p);
	}	
	for(int i = 1; i <= n; i++){
		scanf("%d", &objects[i].a);
	}
	sort(objects + 1, objects + n + 1);
	for(int i = 1; i <= n; i++){
		pref[i] = pref[i - 1] + objects[i].a;
		maxp[i] = max(maxp[i - 1], objects[i].p);
	}
	for(int i = n; i >= 1; i--){
		maxv[i] = max(maxv[i + 1], objects[i].p * 2 + objects[i].a);
	}
	for(int i = 1; i <= n; i++){
		printf("%d\n", max(pref[i - 1] + maxv[i], pref[i] + maxp[i] * 2));
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值