输入:
样例1
3
3 2 1
样例2
5
3 4 5 4 3
输出:
样例1
6
样例2
28
思路: 核心:贪心 / 单调栈
首先分析题意,找最大值,尽可能让k * (x+y)大。
对于x==y ,我们可以在输入数据时候,枚举特判掉。
剩下x!=y的情况,不妨设 kx < ky,对于kx来说y的k值已经没用了,k * ( x+y)中k和x已经定了,剩下让y尽可能让下标大一点。
法一:排序 + 贪心 O(nlogn)
先按k值从大到小排序,把k值大的先作为x(默认kx<ky),因为k越大可以选择的y越少(因为选的y要满足 ky > kx ),枚举满足的y就好了,然后一边更新答案,一边更新y的最大值。
说是枚举y,其实只要保留最大下标y即可。
法二:单调栈维护k O(n)
ps:此题有加强版,只能用O(n) 过
边输入边存,因为是顺序输入,所以后来者的下标肯定越大,放在栈顶,单调递减栈维护ky值从小到大,栈顶ky小于当前的kx,所以kx的最大y(即最大下标在栈顶取,因为顺序输入)。
具体维护单调栈的操作如下:
设已读的元素为y,未读或者正在读的元素为x,当kx < ky时,直接入栈,更新答案,当kx ≥ ky时,一边更新答案,一边将 ky ≤ kx 的元素退栈,最后一次退栈后,还要和栈内的元素再维护一下最大值。
法一:贪心+排序
//贪心
#include<bits/stdc++.h>
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
using namespace std;
typedef long long ll;
const int N=1e6+10;
struct ty
{
int id;//下标
int k;//权值
}a[N];
bool cmp(ty a,ty b)//按照k值排序
{
if( a.k!= b.k ) return a.k>b.k;
return a.id > b.id;
}
int main()
{
int n;
ll ans=0;
scanf("%d",&n);
_for(i,1,n)
{
scanf("%d", &a[i].k);
a[i].id = i;
ans = max( ans, 1ll * a[i].k * i);
}
sort( a+1 ,a+1+n, cmp);
int maxn=0;//最大下标
for(int i=1 ;i<=n ;i++)
{
//更新答案
ans = max(ans ,1ll * (a[i].id + maxn) * a[i].k );
maxn = max( maxn, a[i].id );//维护最大下标
}
printf("%lld\n",ans);
return 0;
}
法二:单调栈
//单调栈
#include<bits/stdc++.h>
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
using namespace std;
typedef long long ll;
const int N=1e6+10;
int a[N];
vector <int> st[2]; //0值 1下标
int main()
{
int n;
ll ans=0;
scanf("%d",&n);
_for(i,1,n)
{
scanf("%d", &a[i]);
ans = max( ans, 1ll * a[i] * i);
}
//单调栈维护k
//栈顶下标必定最大,栈里维护k递减且栈顶k小于当前k
_for(i,1,n)
{
while( !st[0].empty() && a[i]>=st[0].back() )
{
int x = st[0].back();
int y = st[1].back();
ans = max( ans ,1ll* x * (y+i) );
st[0].pop_back();
st[1].pop_back();
}
if( !st[0].empty())
{
int x = st[0].back();
int y = st[1].back();
ans = max( ans ,1ll* a[i] * (y+i) );
}
st[0].push_back(a[i]);
st[1].push_back(i);
}
printf("%lld\n",ans);
}