您的位置:首页 > Web前端

习题 8-18 UVA - 1619 Feel Good 感觉不错 (容斥定理)

2016-05-20 21:46 405 查看
大体题意:

给你一个长度为n (n <= 100000)的正整数序列ai,求出一段连续子序列a1,,ar 使得 (a1 + a2+ a3 +... ar) * min (a1,a2,a3,,,,ar)尽量大 如果多解任意输出一个!

思路:

思路是比较清晰的,就是有坑:

用sum[i]表示 1 ...i 的和,l[i],r[i],分别表示以第 i 个数字为最小值所能扩展的最左端和最右端!

最后遍历i 更新ans

ll ans = a[i] * (sum[r[i]] - sum[l[i]-1]);

取个最大值即可!

求l 和r 数组还不能暴力求,这样会超时。

for (int i = 1; i <= n; ++i)

while(a[l[i]-1] >= a[i])l[i] = l[l[i]-1];

这样求得话 因为遍历到i 时,l[i] 肯定等于i 的,所以当i的位置前一个数值大于等于 第i 个数值时,第i个数值就应该扩展到 第i个数 前一个位置的数 所能扩展到的位置!

r数组 同理。

最后是更新ans

记录下标时 pl 记录l pr 记录r 他们必须初始化,因为刚上来可能是0嘛! 数据问题!

然后MAX 初始化为0 在与ans 不断比较就可以了

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fin freopen("cin.txt","r",stdin);
#define fout freopen("out.txt","w",stdout);
using namespace std;
typedef long long ll;
const int INF = 1e9+5;
const int maxn = 100000 + 10;
int n,a[maxn];
ll sum[maxn];
int pl,pr,cnt;
int l[maxn],r[maxn];
int main(){
//    fin;
while(scanf("%d",&n) == 1){
for (int i = 1; i <= n; ++i){
scanf("%d",&a[i]);
sum[i] = sum[i-1] + a[i];
l[i] = r[i] = i;
}
a[0] = -INF;
a[n+1] = -INF;
for (int i = 1; i <= n; ++i)
while(a[l[i]-1] >= a[i])l[i] = l[l[i]-1];
for (int i = n; i >= 1; --i)
while(a[r[i]+1] >= a[i])r[i] = r[r[i]+1];
//        printf("start\n");
//        for (int i = 1; i <= n; ++i)printf("%d ",a[i]);
//        printf("\n");
//        for (int i = 1; i <= n; ++i)printf("%d ",l[i]);
//        printf("\n=\n");
//        for (int i = 1; i <= n; ++i)printf("%d ",r[i]);
//        printf("\n");
ll MAX = 0;
pl = pr = 1;
for (int i = 1; i <= n; ++i){
ll ans = a[i] * (sum[r[i]] - sum[l[i]-1]);
if (ans > MAX){
//                printf("%lld\n",ans);
MAX = ans;
pl = l[i];
pr = r[i];
}
}
if (cnt++)printf("\n");
printf("%lld\n%d %d\n",MAX,pl,pr);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: