标签:acm
| Time Limit: 3000MS | Memory Limit: 65536K | |
| Total Submissions: 9186 | Accepted: 2509 | |
| Case Time Limit: 1000MS | Special Judge |
Description
Input
Output
Sample Input
6 3 1 6 4 5 2
Sample Output
60 3 5
Source
给定一序列,求一个子序列要求子序列中的和与子序列中的最小数乘积最大,输出最大乘积以及子序列的起始位置。
我们可以转化为求序列中的每一个数所属于哪个区间,保证该数在这个区间内是最小的,并且这个区间长度最大(比如 3 4 2 6, 2所属区间可以为(4,2)也可以为(4,2,6),我们要求最大的(3,4,2,6),因为要保证子序列的和最大)。
这样求出每个数的所属区间,比较一下就可以了。传统的求每个数所属区间时间复杂度高O(n2),可以采用动规的思想,也可以叫回溯,也和KMP的next数组思想有点像
求第i个数所属区间的左端点L[i]时,L[i]肯定小于等于i,所以把i从第i-1个数(i前面的)开始比较,如果第i-1个数比第i个数小,那么肯定L[i] = i+1,第i-1个数小于第i个数,肯定不会在第i个数所属的区间,如果第i-1个数比第i个数大,那么就用第i个数与 L[ i -1] -1 (第i-1个数所属区间左端点的前一个数)进行比较。
求右端点的思想和求左端点的思想是一样的,逆向求。
代码:
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const int maxn=100010;
int s[maxn];//存放第i个元素在某个区间内为最小值的区间左端点
int e[maxn];//存放第i个元素在某个区间内为最小值的区间右端点
int node[maxn];//存放输入元素
long long ans;//输出结果
long long sum[maxn];//存放第1到i个元素的和
int S=1,E=1;//输出所求区间的起始位置
int main()
{
int n;cin>>n;
sum[0]=0;//以下三行初始化为后面做准备
node[0]=-1;
node[n+1]=-1;
for(int i=1;i<=n;i++)//求第1到i个元素和,并且求出第i个元素所属区间的左端点
{
scanf("%d",&node[i]);
sum[i]=sum[i-1]+node[i];//求和
for(int j=i-1;j>=0;)//第i个元素从它前面的第一个元素比较
{
if(node[i]>node[j])
{
s[i]=j+1;//肯定是j+1,因为第j个元素肯定不在第i个元素所属区间内,得保证第i个元素在所属区间内为最小值
break;
}
else
j=s[j]-1;//回溯,从第j个元素所属区间的前面第一个开始比较,如 1 5 4 2, 2<4,4所属区间为(5,4),s[j]=2,所以与第一个元素1比较
}
}
for(int i=n;i>=1;i--)//逆向求第i个元素所属区间的右端点
{
for(int j=i+1;j<=n+1;)//从i后面的第一个元素比较。
{
if(node[i]>node[j])//原理同上
{
e[i]=j-1;
break;
}
else
j=e[j]+1;
}
}
long long temp=0;
ans=-1;
for(int i=1;i<=n;i++)
{
temp=(sum[e[i]]-sum[s[i]-1])*node[i];
if(temp>ans)
{
ans=temp;
S=s[i];
E=e[i];
}
}
cout<<ans<<endl;
cout<<S<<" "<<E<<endl;
return 0;
}
[ACM] POJ 2796 Feel Good (求序列中子序列的和与子序列中的最小数最大乘积)
标签:acm
原文地址:http://blog.csdn.net/sr_19930829/article/details/38012777