• P4890 Never·island


    传送门

    考虑把总区间长度减去最多能减少的区间长度

    把所有区间离散化,对每一小段计算贡献

    分类讨论一波,对于边界 $i,i+1$ ,设它们之间距离 $d$,$i$ 属于 $x$ 考察队的边界,$i+1$ 属于 $y$ 考察队的边界:

      $i$ 为左边界,$i+1$ 为左边界,显然如果给 $x$ 钥匙就可以让这一段时间关门,给 $x$ 的贡献 $val[x]$ 加上 $d$ ,表示给 $x$ 钥匙得到的贡献

      $i$ 为左边界,$i+1$ 为右边界,如果 $x=y$ ,显然给 $x$ 钥匙就可以让这段时间关门,那么给 $val[x]$ 加 $d$

        如果不为同一个考察队,那么如果要得到这一段的价值就必须同时给两个考察队钥匙,就从 $x$ 往 $y$ 连一条边权为 $d$ 的单向边,表示 $x,y$ 都有钥匙得到的贡献

      $i$ 为右边界,$i+1$ 为左边界,那么这一段显然关门就好,直接给答案加上 $d$

      $i$ 为右边界,$i+1$ 为右边界,那么给 $y$ 钥匙就可以让这段时间关门,所以 $val[y]+=d$

      (注意此时不用考虑 $x$ 能不能进来,$x$ 在之前就已经考虑过了)

    发现这的图是由几条链组成的,所以可以对每条链分别 $dp$

    设 $f[i][j][1/0]$ 表示前 $i$ 个点,给了 $j$ 个点钥匙,当前点 $i$ 选$/$不选的最大代价

    具体实现起来还有点难度,看代码吧

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<vector>
    #include<map>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=5e3+7,INF=1e9+7;
    int n,K,tot,ans;
    int pos[N],val[N],eval[N],nex[N];//eval是边的价值,nex存边
    map <int,int> mp;//维护某个位置的编号
    int id[N],f[2007][2007][2];//id是新的编号(同一条链的编号连续),f是dp数组
    bool vis[N];//是否被重新编号过
    void dfs(int x) { vis[ id[++tot]=x ]=1; if(nex[x]) dfs(nex[x]); }//给一条链的每个节点编号
    int main()
    {
        n=read(),K=read();
        int l,r;
        for(int i=1;i<=n;i++)
        {
            l=read(),r=read();
            mp[l]=i<<1; mp[r]=i<<1|1;//编号
            pos[i]=l; pos[n+i]=r;//存位置,注意位置都是左闭右开的!
        }
        sort(pos+1,pos+(n<<1)+1);//排序
        int res=0;
        for(int i=1;i<(n<<1);i++)//对每一小段分类讨论
        {
            l=pos[i],r=pos[i+1];
            int ld=mp[l],rd=mp[r];
            if( !(ld&1) )//i为左边界
            {
                if( !(rd&1) ) val[ld>>1]+=r-l;//i+1为左边界
                else//i+1为右边界
                {
                    if( (ld|1)==rd ) val[ld>>1]+=r-l;//如果属于同一点
                    else nex[ld>>1]=(rd>>1),eval[ld>>1]=r-l;
                }
            }
            else//i为右边界
            {
                if( !(rd&1) ) res+=r-l;//i+1为左边界
                else val[rd>>1]+=r-l;//i+1为右边界
            }
        }
        for(int i=n<<1;i;i--)//注意从后往前找,要让链的节点编号连续
        {
            int d=mp[pos[i]];
            if( !(d&1) && !vis[d>>1] ) dfs(d>>1);
        }
        memset(f,~0x3f,sizeof(f));//记得初始化
        f[tot+1][0][0]=0;
        for(int i=tot;i;i--)
        {
            f[i][0][0]=0;
            for(int j=1;j<=min(K,tot-i+1);j++)
            {
                f[i][j][0]=max(f[i+1][j][0],f[i+1][j][1]);
                f[i][j][1]=max(f[i+1][j-1][0], f[i+1][j-1][1]+eval[ id[i] ] )+val[ id[i] ];
            }
        }
        printf("%d", pos[n<<1]-pos[1]-res-max(f[1][K][0],f[1][K][1]) );
        return 0;
    }
  • 相关阅读:
    linux安装JRE和Tomcat
    微信公众号授权登录
    linux Nginx设置多级域名
    bootstrap制作收藏夹导航
    js下拉菜单
    QQ授权登录
    centos7.6下安装LNMP环境(linux+nginx+mysql5.7+PHP)
    typora快捷键(转载)
    旋转魔方(2)-添加照片
    test
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/10669973.html
Copyright © 2020-2023  润新知