P2672 推销员 优先队列 + 贪心

~~~题面~~~

题解:

我会说我想这道普及组题都想了好久么。。。。

不熟练的普及组选手.jpg

最后随便猜了一个结论居然是对的。。。

放结论:

假设x = i的最优决策为f[i], 那么f[i + 1]一定包括f[i].

也就是说f[i+1]一定可以由f[i]推出来。

所以f[i]一定是由f[i+1]中选定的所有点中去掉一个得来的。

即下一次选择一定是基于上一次选择的。。。

至于证明嘛。。。感性理解吧。

因为选哪户人家推销其实相互没太多联系。而且选i个推销肯定选满是最划得来的。。。。

大概是这么理解的吧。(反向理解可能更加好理解一点)

所以有了这个结论就很好解决了。

因为f[n]的最优决策肯定是n个点全选。

所以我们从这个已知的往前推。

于是我们来观察这n个点,到底删掉哪个减掉的疲劳值最小呢?

首先选中的这些点可以看做一个有序点集,位置由s[i]的大小所决定。

因此我们记last[x]为在x前面的第一个点的id, Next[x]为在x后面的第一个点的id。

于是有两种情况:

1,删去的点在末尾。

  此时减掉的疲劳值为a[x] + 2 * (s[x] - s[last[x]])

2,删去的点在中间。

  那么此时对走路的距离没有任何影响,因此我们只需要减a[x]即可。

值得注意的是,当末尾点k为a[i]最小的点时, 我们需要再取出次小的点来做比较,因为a[k]虽然最小,但由于要走很多路,所以a[k] + 2 * (s[k] - s[last[k]])并不一定小于a[i](i < k, 且i ∈ 点集),因此我们需要进一步比较再做取舍。

而k不是a[i]最小的点时,因为a[k]就已经大于a[i]了,所以减去的疲劳值肯定要大于a[i]了,因此直接删最小的点就可以了。

这里的最小和次小由于要支持插入,删除,查询,因此我们使用优先队列来实现。

为了方便查询元素的id,,,于是我又建了一个结构体、。

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define R register int
 4 #define LL long long
 5 #define getchar() *o++
 6 #define AC 100100
 7 char READ[5001000], *o = READ;
 8 int n;
 9 int s[AC], a[AC], ans[AC], Next[AC], last[AC], r;
10 struct node{
11     int w, id;
12 };
13 
14 struct cmp{
15     bool operator () (node a, node b)
16     {
17         return a.w > b.w;
18     }
19 };
20 
21 priority_queue <node, vector<node>, cmp> q;
22 
23 inline int read()
24 {
25     int x = 0; char c = getchar();
26     while(c > '9' || c < '0') c = getchar();
27     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
28     return x;
29 }
30 
31 void pre()
32 {
33     n = read();
34     for(R i = 1; i <= n; i++) s[i] = read();
35     for(R i = 1; i <= n; i++) 
36     {
37         a[i] = read(); 
38         q.push((node){a[i], i});    
39         ans[n] += a[i];    
40         last[i] = i - 1;
41         if(i != n) Next[i] = i + 1;
42     }
43     ans[n] += s[n] * 2, r = n;
44 }
45 
46 void work()
47 {
48     int b = n - 1;node x, now;
49     for(R i = b; i; i--)
50     {
51         x = q.top();
52         if(x.id == r)
53         {
54             q.pop();
55             now = q.top();
56             if(now.w < x.w + (s[x.id] - s[last[x.id]]) * 2)
57             {
58                 q.pop();
59                 Next[last[now.id]] = Next[now.id];
60                 last[Next[now.id]] = last[now.id];
61                 ans[i] = ans[i + 1] - now.w;
62                 q.push(x);
63             }
64             else//不然就删后者
65             {
66                 Next[now.id] = 0;
67                 ans[i] = ans[i + 1] - x.w - 2 * (s[x.id] - s[last[x.id]]);
68                 r = last[x.id];//移动末项
69             }
70         }
71         else//不然直接就删中间的了
72         {
73             q.pop();
74             Next[last[x.id]] = Next[x.id];
75             last[Next[x.id]] = last[x.id];
76             ans[i] = ans[i + 1] - x.w;
77         }
78     }
79     for(R i = 1; i <= n; i++) printf("%d
", ans[i]);
80 }
81 
82 int main()
83 {
84     freopen("in.in", "r", stdin);
85     fread(READ, 5000000, 1, stdin);
86     pre();
87     work();
88     fclose(stdin);
89     return 0;
90 }
View Code
原文地址:https://www.cnblogs.com/ww3113306/p/9315731.html