题目
问题描述
阿明是一名推销员,他奉命到螺丝街推销他们公司的产品。
螺丝街是一条死胡同,出口与入口是同一个,街道的一侧是围墙,另一侧是住房。螺丝街一共有 N N N 家住房,第 i i i 家住户到入口的距离为 S i S_i Si 米。由于同一栋房子里可以有多家住户,所以可能有多家住房与入口的距离相等。
阿明会从入口进入,依次向螺丝街的 X X X 家住房推销产品,然后再原路走出去。阿明每走 1 1 1 米就会积累 1 1 1 点疲劳值,向第 i i i 家住房推销产品会积累 A i A_i Ai 点疲劳值。阿明是工作狂,他想知道,对于不同的 X X X ,在不走多余路的前提下,他最多可以积累多少点疲劳值。
输入格式
第一行有一个正整数
N
N
N ,表示螺丝街住房的数量。
接下来的一行有
N
N
N 个正整数,其中第
i
i
i 个整数
S
i
S_i
Si 表示第
i
i
i 家住户到入口的距离。数据
保证
S
1
≤
S
2
≤
…
≤
S
n
≤
1
0
8
S_1\le S_2≤…≤S_n≤10^8
S1≤S2≤…≤Sn≤108 。
接下来的一行有
N
N
N 个整数,其中第
i
i
i 个整数
A
i
A_i
Ai 表示向第
i
i
i 户住户推销产品会积累的疲
劳值。数据保证
A
i
<
1
0
3
Ai<10^3
Ai<103 。
输出格式
输出
N
N
N 行,每行一个正整数,第
i
i
i 行整数表示当
X
=
i
X=i
X=i 时,阿明最多积累的疲劳值。
数据范围与提示
对于
20
%
20\%
20%的数据,
1
≤
N
≤
20
1≤N≤20
1≤N≤20 ;
对于
40
%
40\%
40%的数据,
1
≤
N
≤
100
1≤N≤100
1≤N≤100 ;
对于
60
%
60\%
60%的数据,
1
≤
N
≤
1000
1≤N≤1000
1≤N≤1000 ;
对于
100
%
100\%
100%的数据,
1
≤
N
≤
100000
1≤N≤100000
1≤N≤100000 。
思路
说明:为了行文方便,以下称 “一户人家” 为 “点” ,将要推销的点的集合为 X X X 。
因为不走回头路,所以行走的疲劳值只有
2
max
x
∈
X
S
x
2\max_{x\in X} S_x
2maxx∈XSx ,而推销疲劳值为
∑
x
∈
X
A
x
\sum_{x\in X}A_x
∑x∈XAx ,所以有:
a
n
s
=
2
max
x
∈
X
S
x
+
∑
x
∈
X
A
x
ans=2\max_{x\in X}S_x+\sum_{x\in X}A_x
ans=2x∈XmaxSx+x∈X∑Ax
注意到 max x ∈ X S x \max_{x\in X}S_x maxx∈XSx 的变化是非常难的,所以,我们不妨假设一组解。其深层原因是,我们要最大化 a n s ans ans,恰好它是由 max S x \max S_x maxSx 决定的,二者统一。
在这组解中,我们将 S S S 最大的点固定,剩下的点就可以任选,目的是让 ∑ A \sum A ∑A 最大。
很显然,剩下的点应当尽量去选择 A A A 比较大的,因为 S S S 已经不归它们管了。
所以,我们最多只需要一个点去得到较大的 S S S ,于是就至少会选前 i − 1 i-1 i−1 大的 A A A 。
固定了 i − 1 i-1 i−1 个极大的 A A A 之后,剩下的一个就应该综合考虑 S + A S+A S+A 了。这可以用 O ( n ) \mathcal O(n) O(n) 的预处理得到。
当然,也有可能不考虑 S + A S+A S+A ,直接全部选最大的 A A A , S S S 就听天由命了。还是可以预处理。
上面这种情况的本质是, S + A S+A S+A 中的 S S S 却没有已经固定的大 A A A 厉害。毕竟 S + A S+A S+A 的考虑是比较狭隘的——它只考虑不属于前 i i i 大的 A A A 的点。
代码
#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;
struct Home{
int s,a;
bool operator<(const Home &x)const{
return a>x.a;
}
};
const int MAXN=100000;
int suma[MAXN+5],s_and_a[MAXN+5],maxs[MAXN+5];
Home home[MAXN+5];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&home[i].s);
for(int i=1;i<=n;++i)
scanf("%d",&home[i].a);
sort(home+1,home+1+n);
for(int i=1;i<=n;++i)
{
maxs[i]=max(maxs[i-1],home[i].s);
suma[i]=suma[i-1]+home[i].a;
}
for(int i=n;i>=1;--i)
s_and_a[i]=max(s_and_a[i+1],home[i].s*2+home[i].a);
for(int i=1;i<=n;++i)
printf("%d\n",max(maxs[i]*2+suma[i],s_and_a[i]+suma[i-1]));
return 0;
}
后记
我的所有博客中(我还是写了不少的),阅读比较多的竟然是这几篇:
- 巴拉巴拉(记不清名字了),是一道有点意思的黑题。
- 这篇博客,是一道绿题……
- 普林姆正确性的证明,而最小生成树的板题是黄色的……
我无话可说。您开心就好。