1月30日
Description
WYF从小就爱乱顶,但是顶是会造成位移的。他之前水平有限,每次只能顶出的位移,也就是从一个整点顶到另一个整点上。我们现在将之简化到数轴上,即从 一个整点可以顶到与自己相隔在之内的数轴上的整点上。现在WYF的头变多了,于是他能顶到更远的地方,他能顶到任意整点上。现在他在玩一个游戏,这个游 戏里他只能向正方向顶,同时如果他从i
顶到j
,他将得到 \(a[j] \times (j - i)\) 的分数,其中a[j]
是j
点上的分数,且要求j > i
, 他最后必须停在n
上。
现给出1~n
上的所有分数,原点没有分数。他现在在原点,没有分。WYF想知道他最多能得多少分。
Input
第一行一个整数n。
第二行有n个整数,其中第i个数表示a[j]
。
Output
一个整数,表示WYF最多能得到的分数。
Sample Input
3
1 1 50
Sample Output
150
Data Constraint
对于60%的数据,n<=1000;
对于100%的数据,n<=100000,0<=a[j]
<=50。
Solution
- 【我的解法】
比赛时我没想太多,就直接看着60%分数 \(n^2\) dp水了60分。
#include<cstdio>
#include<algorithm>
using namespace std;
int n,a[100001],f[100001];
int main()
{
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int j=1;j<=n;j++)
for(int i=0;i<j;i++)
f[j]=max(f[j],(j-i)*a[j]+f[i]);
printf("%d",f[n]);
fclose(stdin);
fclose(stdout);
return 0;
}
- 【玄学正解】
正解是建立在刚刚的dp之上的。要进行非常玄学的优化,叫斜率优化。我引用一下题解。
对于到达的每个点,若要知道应该从哪个点过来,则要比较从两个不同地方过来的答案进行比较。最后比较式整理成类似于 \(\dfrac{f[i] – f[j]}{i – j} > ?\) 的形式。之后可以将
(i, f[i])
变为二维上的一点,比较式就成为了两点间的斜率是否大于一个数。由于 i 是从小到大枚举的则计算出一个f[i]
时就把它插入到二维平面里,之后只需维护斜率下降的一段(斜率上升的一段不需要考虑),用栈来存储点,维护凸包总时间复杂度为 \(O(n)\) ,找最好节点用二分。总时间复杂度为 \(O(n\ log\ n)\) 。
\(by\) 北师大实验
显然我没有看懂,于是我询问了另一位大佬,他告诉我两个字:贪心。
于是我就AC了。。。
#include<cstdio>
using namespace std;
int n,a[100001],ans,curr;
int main()
{
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
curr=n;
for(int i=n;i>=0;i--)
{
if(a[i]<a[curr])
continue;
ans+=(curr-i)*a[curr];
curr=i;
}
ans+=curr*a[curr];
printf("%d",ans);
fclose(stdin);
fclose(stdout);
return 0;
}
斜率优化这玩意我还是先放一放。。。