【Codeforces 559E】Gerald and Path

动态规划

题目大意:

给定数轴 $n$ 条线段的长度和一个端点的位置,你要确定每条线段另一个端点的位置,使得被这些线段覆盖的总长度最长。求最长的总长度。

题解:

为了避免线段重合的问题,我们对于线段 $[l,r]$,其前缀 $[l,l],[l,l+1],[l,l+2],\cdots,[l,r-1]$ 也可以产生贡献。即若选择这条线段,则 $l\sim r$ 每个位置都要更新贡献,这里的长度为实际算上去的长度而非线段原来的长度。

离散化,对线段按照端点位置排序,令 $f_{i,j}$ 表示前 $i$ 条线段,最右端覆盖到位置 $j$ 时覆盖的最长长度。

考虑这个 $[l,p],[p,r]$ 二选一的两种方法:

向右放(选择 $[p,r]$)。这种情况与之前不会产生冲突,直接转移。$f_{i,j}=\max\{f_{i-1,p}+\mathrm{dist}(p,j)\},j\in[p,r]$。

向左放(选择 $[l,p]$)。这种情况就会产生一个问题:它左边可能存在一条或多条往右的线段,使得右端点在更右边。

我们直接枚举 $k$ 并令 $k\sim i-1$ 这些线段都向右放。令 $R$ 表示右端点最右的位置。则 $f_{i,j}=\max\{f_{k-1,l}+\mathrm{dist}(l,R)\}$。

正确性:如果中间有线段往左放的,那我们这里计算的时候,那个往左放的线段只算进了它的一个前缀的贡献,其冲突部分算在后面这条线段。因此贡献是对的。

直接枚举的复杂度为 $O(n^3)$。考虑 $k$ 从大到小枚举,更新 $R$ 的值。令 $g[R]$ 表示右端点为 $R$ 时的最大答案。

我们直接算出 $g[R]$ 后,还要对其做一遍后缀的处理。就是相当于取前缀计算贡献。具体可以见代码里的处理。

最后每次还要对 $f_i$ 求前缀最大值。

时空复杂度 $O(n^2)$。

Code:

#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
vector<int>vec;
const int N=105;
struct lne{
    int l,r,p;
    inline bool operator<(const lne&rhs)const{return p<rhs.p;}
}d[N];
int n,f[N][N*3],g[N*3];
inline void find(int&x){x=lower_bound(vec.begin(),vec.end(),x)-vec.begin();}
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n;
    vec.push_back(-1e9);
    for(int i=1;i<=n;++i){
        int a,l;
        cin>>a>>l;
        d[i]=(lne){a-l,a+l,a};
        vec.push_back(d[i].l);
        vec.push_back(d[i].r);
        vec.push_back(d[i].p);
    }
    sort(vec.begin(),vec.end());
    vec.erase(unique(vec.begin(),vec.end()),vec.end());
    for(int i=1;i<=n;++i)find(d[i].l),find(d[i].r),find(d[i].p);
    const int m=vec.size()-1;
    sort(d+1,d+n+1);
    for(int i=1;i<=n;++i){
        memcpy(f[i],f[i-1],sizeof*f);
        int L=d[i].l,R=d[i].r,p=d[i].p;
        //left
        memset(g,0,sizeof g);
        int nr=p;
        g[nr]=f[i-1][L]+vec[nr]-vec[L];
        for(int j=i-1;j;--j){
            nr=max(nr,d[j].r);
            g[nr]=max(g[nr],f[j-1][L]+vec[nr]-vec[L]);
        }
        for(int j=m;j>=L;--j){
            f[i][j]=max(f[i][j],g[j]);
            g[j-1]=max(g[j-1],g[j]-vec[j]+vec[j-1]);
        }
        //right
        for(int j=p;j<=R;++j)f[i][j]=max(f[i][j],f[i-1][p]+vec[j]-vec[p]);
        for(int j=1;j<=m;++j)f[i][j]=max(f[i][j],f[i][j-1]);
    }
    cout<<f[n][m]<<'\n';
    return 0;
}