最大连续和

2013: 最大连续和(mx)

时间限制: 8 Sec   内存限制: 128 MB
提交: 27   解决: 20
[ 提交][ 状态][ 讨论版]

题目描述

一个长度为n数组A的最大连续和,是指所有满足1≤L≤R≤n的L和R中A[L]+A[L+1]+...+A[R]的最大值。

一次交换操作是指:

(1) 选择两个下标i和j(i ≠ j)

(2)进行赋值,tmp=A[i];A[i]=A[j],A[j]=tmp;

给定一个长度为n的数字,最多进行m次交换操作后,该数组的最大连续和。

输入

第一行两个两个整数n和m。

第二行n个数字,表示数组中的元素。

输出

输出题目要求的答案。

样例输入

10 2
10 -1 2 2 2 2 2 2 -1 10

样例输出

32

提示

样例输入2:

5 10

-1 -1 -1 -1 -1

样例输出2:

-1

对于前20% 的数据,m的值为0; 

对于前30% 的数据,m的范围[0,1];

对于前40% 的数据,n的范围 [1,100],m的范围[0,10];

对于前50% 的数据,n的范围 [1,200],m的范围[0,20];

对于前60% 的数据,n的范围 [1,300],m的范围[0,30];

对于前70% 的数据,n的范围 [1,500],m的范围[0,50];

对于前90% 的数据,n的范围 [1,1000],m的范围[0,100];

对于前100% 的数据,n的范围 [1,1100],m的范围[0,100];



嗯……这道其实说白了是道想法题,本身难度并不是很大,主要考的是思维能力。首先,如果没有交换这个操作,那么这就是道简单的dp,求最大连续和。而本题则多了一个交换的操作。首先,我想到能不能先用dp求出最大连续和,然后将子序列中的小值和子序列外的大值进行交换,来使和更大。这差不多是个贪心,然而,事实证明,这是个错误贪心。如:1 50 -100 50 这个数列求出的最大连续和为1+50=51,再将1与另一个50进行交换,最大值变为50+50=100,然而,正确答案应该是先选1 50 -100,然后将-100与50交换,最大值为1+50+50=101。所以,这是个错误贪心。接下来,别很容易想到暴力的打法,枚举l和r(等于枚举区间),然后将当前区间中的数建立小根堆,区间外的数建立大根堆,然后比较、交换直到大根堆中的最大值小于小根堆中的最小值。这是个n^3 log(n) 的算法,这是个不错的算法,但难以过掉全部的数据。而主要问题的所在就是在与堆的操作需要n log (n)的时间复杂度,能不能把其优化成n的复杂度甚至更少。所以,我们可以想到将堆舍去。直接在刚开始对原数列记录ID并进行排序(从大到小和从小到大都可以,只是在后面的写法上有一点不同)。进行过预处理后,只要在枚举区间的循环内将排好序的数列从大到小扫一遍,如果当前的数的ID在区间内,则答案直接加上该数,如果该数不再区间内,则可交换次数减一,并将该数加入答案(如果可交换次数已经为0则跳过该数)。最后再将答案与max比较,最后输出max即可。(这样可以过掉,但并非最快的算法,正解要用到链表,很难打)。

#include<cstdio>
int a[10000],b[10000];
void kp(int l,int r) //结构体不会用所以手打快排了,这里可以用结构体替代
{
  int i=l;
  int j=r;
  int x=a[(l+r)/2];
  int x1=b[(l+r)/2];
  int t=0;
  while (i<=j)
  {
    while (a[i]<x || (a[i]==x && b[i]>x1)) i++;
    while (a[j]>x || (a[j]==x && b[j]<x1)) j--;
    if (i<=j){
        t=a[i];
        a[i]=a[j];
        a[j]=t;
        t=b[i];
        b[i]=b[j];
        b[j]=t;
        i++;
        j--;
      }
  }
  if (l<j) kp(l,j);
  if (i<r) kp(i,r);
}
int main()
{
    int n,m,n1,m1,ans=0,max1=0;
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
    {
      scanf("%d",&a[i]);
      b[i]=i;
    } 
    kp(1,n);  
    for (int i=1;i<=n;i++)
     for (int j=i;j<=n;j++)
      {
        ans=0;
        n1=j-i+1;//区间内可放的数的个数
        m1=m;//可交换次数
       for (int k=n;k>=1;k--)
        {
         if (n1==0) break;
         if (b[k]>=i && b[k]<=j) { n1=n1-1;ans+=a[k];}
         else { if (m1>0) {m1=m1-1;ans+=a[k];n1=n1-1;  } } //不在区间内的情况  
            }
        if (ans>max1) max1=ans;
      }
     printf("%d",max1);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值