• P1484 种树


    题目描述

    cyrcyr今天在种树,他在一条直线上挖了n个坑。这n个坑都可以种树,但为了保证每一棵树都有充足的养料,cyrcyr不会在相邻的两个坑中种树。而且由于cyrcyr的树种不够,他至多会种k棵树。假设cyrcyr有某种神能力,能预知自己在某个坑种树的获利会是多少(可能为负),请你帮助他计算出他的最大获利。

    输入输出格式

    输入格式:

    第一行,两个正整数n,k。

    第二行,n个正整数,第i个数表示在直线上从左往右数第i个坑种树的获利。

    输出格式:

    输出1个数,表示cyrcyr种树的最大获利。

    输入输出样例

    输入样例#1: 
    6 3 
    100 1 -1 100 1 -1
    
    输出样例#1: 
    200

    说明

    对于20%的数据,n<=20。

    对于50%的数据,n<=6000。

    对于100%的数据,n<=500000,k<=n/2,在一个地方种树获利的绝对值在1000000以内。

    Solution:

      本题的二叉堆的做法实在是太巧妙了!!!

      首先,我打了一个普通的$DP$,期望得分$50$,很容易想到状态$f[i][j],;iin[1,n],;jin[1,k]$表示前$i$个位置中了$j$棵树的最大获利,则由题目限制条件不难想到状态转移方程:$f[i][j]=max(f[i-1][j],f[i-2][j-1]+a[i])$,注意下边界和初始状态就有$50$分了。时间空间复杂度都是$O(n^2)$,显然不行。

      此时想了各种奇技淫巧,依然没用。。。还是默默的看了下题解,惊叹于本题的二叉堆做法:

      我们先进行小规模枚举:
      $k=1$时,显然取$n$个数中取最大的即可(暂不考虑全负的情况)。设最大的数是$a[i]$。

      $k=2$时,则有两种可能:1、另取一个与$a[i]$不相邻的$a[j]$。2、取$a[i-1]$和$a[i+1]$。

      我们可以发现:如果$k=1$时最优解为$a[i]$,那么我们便可以把$a[i-1]$和$a[i+1]$进行合并,因为它们要么同时被选,要么同时落选(证明不难,请自行解决)。而且,我们还注意到:当选了$a[i-1]$和$a[i+1]$时,获利便增加了$a[i-1]+a[i+1]-a[i]$。所以当$a[i]$被选时,我们就可以删去$a[i-1]$和$a[i+1]$,并把$a[i]$改成$a[i-1]+a[i+1]-a[i]$(即使为负也没问题,因为下次不会选它,而若为正则等同于选了$a[i-1]$和$a[i+1]$),重新找最大的。

      每次找的都是最大的数,我们便可以使用堆进行操作,直到堆中最大值小于等于$0$或取出$k$个数后停止。复杂度$O(nlogn)$。

    1、先安利一下自己$DP$的代码:

    #include<bits/stdc++.h>
    #define il inline
    #define ll long long
    using namespace std;
    const int N=10005;
    ll n,k,f[N][5000],ans=-100000000;
    int a[N];
    il ll gi(){
        ll a=0;char x=getchar();bool f=0;
        while((x<'0'||x>'9')&&x!='-')x=getchar();
        if(x=='-')x=getchar(),f=1;
        while(x>='0'&&x<='9')a=a*10+x-48,x=getchar();
        return f?-a:a;
    }
    int main(){
        n=gi(),k=gi();
        for(int i=1;i<=n;i++)a[i]=gi();
        for(int i=1;i<=n;i++)
            for(int j=1;j<=k;j++)f[i][j]=-100000000;
        f[1][1]=a[1];
        for(int i=2;i<=n;i++)
            for(int j=1;j<=k&&j<=i;j++){
            if(i-2>0)f[i][j]=max(f[i-1][j],f[i-2][j-1]+a[i]);
            else f[i][j]=f[i-1][j];
            ans=max(f[i][j],ans);
        }
        cout<<ans;
        return 0;
    }

    2、再发一波正解代码:

    #include<bits/stdc++.h>
    #define il inline
    #define ll long long
    using namespace std;
    const int N=500005;
    int n,k;
    ll ans,tot,a[N],l[N],r[N],pos;
    struct node{
        int v,id;
        bool operator < (const node a)const {return v<a.v;}
    }tmp;
    priority_queue<node>q;
    bool vis[N];
    il ll gi(){
        ll a=0;char x=getchar();bool f=0;
        while((x<'0'||x>'9')&&x!='-')x=getchar();
        if(x=='-')x=getchar(),f=1;
        while(x>='0'&&x<='9')a=a*10+x-48,x=getchar();
        return f?-a:a;
    }
    int main(){
        n=gi(),k=gi();
        for(int i=1;i<=n;i++){
            tmp.v=gi(),tmp.id=i;q.push(tmp);
            l[i]=i-1;r[i]=i+1;a[i]=tmp.v;
        }
        r[0]=1,l[n+1]=n;
        while(k--){
            while(vis[q.top().id])q.pop();
            tmp=q.top();q.pop();
            if(tmp.v<0)break;
            ans+=tmp.v;pos=tmp.id;
            a[pos]=a[l[pos]]+a[r[pos]]-a[pos];
            tmp.v=a[pos];
            vis[l[pos]]=vis[r[pos]]=1;
            l[pos]=l[l[pos]],r[l[pos]]=pos;
            r[pos]=r[r[pos]],l[r[pos]]=pos;
            q.push(tmp);
        }
        cout<<ans;
        return 0;
    }
  • 相关阅读:
    list 集合工具类
    Java获取当前系统时间的前N小时时间
    注意点
    小bug
    Linux常用命令
    MySql的执行计划
    【解决问题】引入.vmx文件后,xshell连接Could not connect to ‘192.168.211.132‘ (port 22): Connection failed
    星涛:javaEE学习路线一览
    Vue响应式原理
    Java操作Xml文件
  • 原文地址:https://www.cnblogs.com/five20/p/8955018.html
Copyright © 2020-2023  润新知