zoukankan      html  css  js  c++  java
  • 洛谷P1792——[国家集训队]种树

    传送门:QAQQAQ

    题意:$n$个点中选$m$个不相邻的点,使得这些点不相邻(1和n算相邻),求这些点的最大值

    思路:这不是神仙题不是神仙题……

    刚看到这题觉得不难,好像只要贪心就可以了但贪心不知从何下手——因为取了一个点就会影响其它两个点

    所以我们要用“可以反悔”的贪心,即取完一个点以后,我们要加入一个点,让此操作可以反悔——不取这个点,而取它两边的点,即$val[new]=val[l]+val[r]-val[pos]$,然后取了当前点把它左右点删去即可(因为再取一次当前点就是取它左右点的情况)(有点像网络流里的残量网络)

    这样用一个堆维护,每次取最大值即可

    (证明:因为有可以反悔的操作,所以每种情况都可以选到,不会因为取了当前最大值而排除了全局最优解,而每次取的都是合法的切实最大值,所以最终答案一定是最大值)

    代码:(推荐比赛的时候先写一个较为稳妥的DP)

    #include<bits/stdc++.h>
    using namespace std;
    const int inf=(int)2e8;
    const int N=200005;
    
    int dp[2][2201][1101][2];
    int n,m,a[N],ans=-inf;
    
    void checkmax(int &x,int y)
    {
        if(x<y) x=y;
    }
    
    void subtask1()
    {
        for(int bl=0;bl<=1;bl++)
            for(int i=0;i<=n;i++)
                for(int j=1;j<=m;j++)//之前写成了0 
                    for(int t=0;t<=1;t++) dp[bl][i][j][t]=-inf;
        dp[1][1][1][1]=a[1]; dp[0][1][0][0]=0;
        for(int bl=0;bl<=1;bl++)
        {
            for(int i=2;i<=n;i++)
            {
                for(int j=1;j<=m;j++)
                {
                    if(!(i==n&&bl==1)) dp[bl][i][j][1]=dp[bl][i-1][j-1][0]+a[i];
                    dp[bl][i][j][0]=max(dp[bl][i-1][j][1],dp[bl][i-1][j][0]);//+=!
                    if(j==m)
                    {
                        checkmax(ans,dp[bl][i][j][0]);
                        checkmax(ans,dp[bl][i][j][1]);
                    }
                }
            }
        }
        printf("%d
    ",ans);
    }
    
    struct node{
        int l,r,val,id;
        bool operator < (const node &rhs) const{
            return val<rhs.val;
        }
    }t[N];
    
    int vis[N];
    priority_queue<node> q;
    void subtask2()
    {
        int sum=0;
        for(int i=1;i<=n;i++)
        {
            t[i].l=i-1; t[i].r=i+1;
            t[i].val=a[i]; t[i].id=i;
        }
        t[1].l=n; t[n].r=1;
        for(int i=1;i<=n;i++) q.push(t[i]);
        for(int i=1;i<=m;i++)
        {
            while(vis[q.top().id]) q.pop();
            node f=q.top(); q.pop();
            //取出来的node不能直接用,因为它的l,r可能已经改变 
            vis[t[f.id].l]=vis[t[f.id].r]=1;//可以理解成三个点并成了一个点 
            sum+=f.val;
            t[f.id].val=t[t[f.id].l].val+t[t[f.id].r].val-t[f.id].val;
            t[f.id].l=t[t[f.id].l].l;
            t[f.id].r=t[t[f.id].r].r;
            t[t[f.id].r].l=f.id;
            t[t[f.id].l].r=f.id;
            q.push(t[f.id]);
        }
        cout<<sum<<endl;
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        if(m>n/2)
        {
            puts("Error!");
            return 0;
        }
        if(n<=2100) subtask1();
        else subtask2();
        return 0;
    }
    View Code
  • 相关阅读:
    20190425-快速排序
    ExtJS合同管理信息系统源码
    2013年12月12日16Aspx源码发布详细
    大型商业手机进销存管理系统源码
    大管家固定资产管理系统源码
    地平线开源网站源码Deepsoon v1.2.3
    发票管理系统(Extjs)源码
    国讯通用OA协同办公系统源码
    物流管理系统源码
    2013年10月16Aspx源码发布详情
  • 原文地址:https://www.cnblogs.com/Forever-666/p/11272887.html
Copyright © 2011-2022 走看看