[NOIP2015 普及组] 推销员

一道简单的贪心题。

题意

$x$ 轴上有 $n$ 个点($x_i\in\N^\ast$),每个点有权值 $v_i$,有一小人从原点出发,需要选定 $k$ 个位置进行操作,操作方法是:先走到距离最远的点,操作之,在走回来的路上顺路操作需要操作的点。操作第 $i$ 个点会获得 $v_i$ 的收益,走 $1$ 个单位长度会获得 $1$ 的收益,对每个 $k\in[1,n]$ 分别求出最大收益。

$n\le 10^5$,$x_i\le 10^8$,$v_i\le 1000$。

link: https://www.luogu.com.cn/problem/P2672

source: PJ 2015

做法

容易知道 $k$ 的最优选择(之一)一定是 $k-1$ 的最优选择(之一)的超集。

按 $v_i$ 和 $2x_i+v_i$ 分别维护两个点的 std::set,每次贪心选择即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <cstdio>
#include <set>
#include <algorithm>

namespace mirai {

class node {
public:
int dist, val;
friend bool operator<(const node& x, const node& y);
friend bool operator==(const node& x, const node& y);
} a[100005];

bool operator<(const node& x, const node& y) {
return x.dist == y.dist ? x.val < y.val : x.dist < y.dist;
}
bool operator==(const node& x, const node& y) {
return x.dist == y.dist && x.val == y.val;
}

class comp1 {
public:
bool operator()(const node& x, const node& y) const {
return x.val != y.val ? x.val > y.val : x.dist > y.dist;
}
};
class comp2 {
public:
bool operator()(const node& x, const node& y) const {
return 2 * x.dist + x.val != 2 * y.dist + y.val ? 2 * x.dist + x.val > 2 * y.dist + y.val : !(x < y || x == y);
}
};
std::multiset<node, comp1> s1;
std::multiset<node, comp2> s2;

int main(int argc, char** argv) {
int n;
std::scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
std::scanf("%d", &a[i].dist);
}
for (int i = 1; i <= n; ++i) {
std::scanf("%d", &a[i].val);
}
for (int i = 1; i <= n; ++i) {
s2.insert(a[i]);
}
int nowdist = 0, nowidx = 1;
long long nowans = 0;
for (int i = 1; i <= n; ++i) {
// std::printf("s1: ");
// for (auto i : s1) {
// std::printf("(%d, %d) ", i.dist, i.val);
// }
// std::printf("\ns2: ");
// for (auto i : s2) {
// std::printf("(%d, %d) ", i.dist, i.val);
// }
// std::printf("\n");
int maxs2 = -1, maxs1 = -1;
if (!s2.empty()) {
auto begin = s2.begin();
maxs2 = (begin->dist - nowdist) * 2 + begin->val;
}
if (!s1.empty()) { maxs1 = s1.begin()->val; }
if (maxs2 <= maxs1) {
auto begin = s1.begin();
nowans += begin->val;
s1.erase(begin);
} else {
auto begin = s2.begin();
// std::printf("{%d, %d}\n", begin->dist, begin->val);
nowans += (begin->dist - nowdist) * 2 + begin->val;
nowdist = begin->dist;
while (a[nowidx].dist < begin->dist) {
// std::printf("%d %d %s\n", i, nowidx, s2.find(a[nowidx]) == s2.end() ? "YES" : "NO");
auto find = s2.find(a[nowidx]);
if (find == s2.end()) {
nowidx++;
continue;
}
s2.erase(s2.find(a[nowidx]));
s1.insert(a[nowidx]);
nowidx++;
}
s2.erase(begin);
}
std::printf("%lld\n", nowans);
}
return 0;
}

} // namespace mirai

int main(int argc, char** argv) {
return mirai::main(argc, argv);
}