• bzoj3502[PA2012]Tanie Linie(最大k区间和)


    题意:给定一个长为n的数列,要求选出最多k个不相交的区间(可以不选),使得选中的数字之和最大.(1<=k<=n<=1000000)
    分析:首先我们通过预处理对问题做一些简化.原序列中的0对答案没有影响,可以直接删掉.连续的一段正数或一段负数一定是都选或者都不选,可以合并成一个数字.这样把序列转化成了正数和负数交替出现的形式.如果序列的最左端/最右端是负数,这个负数在最优解当中一定不会被选中,我们可以把它删掉.这样就把序列变成了正负交替,以正数开头和结尾的形式.
    这时若直接考虑选出k个不相交区间,仍然不好做.这里一个关键是先不考虑k的限制得到一个最优解,然后再对这个解进行调整使得它满足k的限制.
    不考虑k的限制,最优解显然就是只选择所有正数,如果这时已经满足k的限制,就直接输出答案,否则我们需要进一步观察性质.
    一开始,怎样修改这个解才能使得区间数目减少1呢?有两种方法:
    1. 将某个正数由选中变为不选中,区间数减1
    2. 将某个负数由不选中变为选中,可以连接两侧的区间使得区间数减1
    显然,不论怎样操作,答案都会变小,这个变小的值就是我们修改状态的数的绝对值,那么第一步可以贪心的,现在的问题是怎样实现连续的贪心,并且每一步操作都让区间数减1.
    考虑连续的三个数字… (左边一堆数)...a,b,c…(右边一堆数)…,不妨设a<0,c<0,b>0,如果b的权值最小,我们在第一步将b从选变成不选,那么之后单独将a或c从不选变成选就不能减少区间个数,我们打个标记将这两种决策删除掉.但这时有另外一种反悔的决策:同时选中abc三个数,这样可以让区间数再次减1.如果中间的数字是负数,也同理会使接下来可行的决策数目减少2,然后增加一种新的可行决策.我们用一个堆维护所有的决策(priority_queue足矣0),用一个链表维护每个位置前后第一个没有被删除的决策的位置,复杂度O(nlogn)
    注意一个边界情况:a,b….(a的左边没有更多数字了,a>0,b<0),如果我们在选b之前将a从选中变为不选中,那么下一步同时选中ab将无法使区间数目减少(a的左边没有一个区间可以和右边连接),此时应当不添加新的决策.(这里我们在选中b之前选中了a,说明ab之和小于0,要么只选a,要么都不选,如果ab都选了一定没有都不选优).如果选a之前选中了b,这时将ab所在的区间变成都不选可以减少区间个数,不需要特殊处理.
    类似于bzoj1150数据备份和bzoj2151种树.细节各种狗,一定要对拍…对拍可以费用流,也可以O(n^2)DP

    #include<cstdio>
    #include<queue>
    using namespace std;
    typedef long long ll;
    const int maxn=1000005;
    ll a[maxn];
    bool del[maxn];
    struct node{
      ll w;int pos;
      node(ll a,int b){
        w=a;pos=b;
      }
      bool operator <(const node &B)const{
        return w<B.w;
      }
    };
    priority_queue<node> q;
    int pre[maxn],nxt[maxn];ll val[maxn];
    int main(){
      int n,k;scanf("%d%d",&n,&k);
      ll x;
      int tot=1;
      scanf("%lld",&a[1]);
      for(int i=2;i<=n;++i){
        scanf("%lld",&x);//printf("%d
    ",x);
        if(x==0)continue;
        if((x>=0)==(a[tot]>=0)||a[tot]==0)a[tot]+=x;
        else a[++tot]=x;
      }
      ll sum=0;int cnt=0;
      for(int i=1;i<=tot;++i){
        if(a[i]>=0){//printf("%lld
    ",a[i]);
          sum+=a[i];cnt++;
        }
      }
      if(cnt<=k){//printf("!");
        printf("%lld
    ",sum);
      }else{
        k=cnt-k;
        int l=1,r=tot;
        if(a[1]<0)l++;
        if(a[tot]<0)r--;
        if(tot==1&&a[1]<0){
          printf("0
    ");
        }else{
          for(int i=l;i<r;++i)nxt[i]=i+1;
          for(int i=l+1;i<=r;++i)pre[i]=i-1;
          for(int i=l;i<=r;++i)val[i]=(a[i]>=0)?(-a[i]):a[i];
          for(int i=l;i<=r;++i)q.push(node(val[i],i));
          while(k--){//printf("!");
            while(del[q.top().pos])q.pop();
            node tmp=q.top();q.pop();
            int i=tmp.pos;//printf("%d
    ",tmp.w);
            sum+=tmp.w;del[pre[i]]=del[nxt[i]]=true;
            if(pre[i]&&nxt[i]){
              val[i]=val[pre[i]]+val[nxt[i]]-val[i];
              q.push(node(val[i],i));
              pre[i]=pre[pre[i]];nxt[i]=nxt[nxt[i]];
              if(nxt[i])pre[nxt[i]]=i;
              if(pre[i])nxt[pre[i]]=i;
            }else{
                      del[i]=true;pre[i]=pre[pre[i]];nxt[i]=nxt[nxt[i]];
              if(nxt[i])pre[nxt[i]]=pre[i];
              if(pre[i])nxt[pre[i]]=nxt[i];
            }
          }
        }
        printf("%lld
    ",sum);
      }
      return 0;
    }
  • 相关阅读:
    详细的描述一个测试活动完整的过程
    黑盒测试的测试用例常见设计方法都有哪些?请分别以具体的例子来说明这些方法在测试用例设计工作中的应用。
    测试计划工作的目的是什么?测试计划文档的内容应该包括什么?其中哪些是最重要的?
    redo log 有什么作用?
    Spring的简介和优点?
    java的语法基础(二)
    相对于Statement,PreparedStatement的优点是什么?
    MyBatis 的好处是什么?
    python中字符串的编码和解码
    相对于Statement,PreparedStatement的优点是什么?
  • 原文地址:https://www.cnblogs.com/liu-runda/p/6412205.html
Copyright © 2020-2023  润新知