CF559E Gerald and Path 题解

Gerald and Path

考虑将所有线段按照固定的那一端从小往大排序,并且对线段的端点离散化。

这之后,设 fi,j 表示当前处理到线段 i,且所有线段中最右的那根的右端点不右于位置 j(即可以在 j 左面或与 j 重合)时的最优答案。

我们考虑,假设我们放了一根线段 [l,r]。因为不知道将来会放什么东西把它盖掉一部分,所以我们干脆在放线段 [l,r] 时,同时也放下线段 [l,l],[l,l+1],[l,l+2],,[l,r1],这样就不用担心被盖掉等讨论了。

于是我们现在考虑处理第 i 根线段。设其向左是 [l,p],向右是 [p,r]

首先,其有可能在接下来(或者在 i 之前就已经)被完全覆盖掉。于是一开始就有 fi1,jfi,j

其次,考虑其向右摆。向右摆,就意味着最右位置一定是 r,若最右位不是 r,则一定在 i 之前还有一条向右摆的线段 [p,r] 满足 rr。但是因为我们已经按照 p 递增排序了,故必有 [p,r][p,r],即 [p,r] 被完全覆盖,我们已经在上面说过了。

则有 fi,rfi1,p+[p,r],因为我们已经令 fi,j 表示“不右于”的最优值,所以就不用费尽心思枚举 i1 时的最右位置了。同时,也不用担心重叠问题,因为按照我们上述讨论,为了避免重叠,我们直接将线段 [l,r] 看作了所有 [l,lr]

但是这也意味着,我们的线段 [p,r] 也要被看作是所有的 [p,pr]。于是我们枚举 j[p,r],则有 fi,jfi1,p+[p,j]

然后就考虑其向左摆了。向左摆,就意味着最右位置不一定是 p,因为完全可以存在一条 [p,r],满足 r>pand,即两者无交。

首先,最右位是 p 的就可以跟之前一样类似地转移,不在话下。

然后,对于最右位不是 p 的,我们枚举一个 j<i,表示最右位是 j 对应的 [p,r](明显这里上述 r>pandp>l 应被满足)。则所有 k(j,i] 都应该向左摆,因为若其向右摆,要么右端点在 r 左边或与 r 重合,被完全包含;要么右端点在 r 右边,则 r 就不是最右位了。

设所有 k(j,i] 的东西中,最左的那个是 [l,p],则整个 [j,i] 中所有线段,加一块可以变成一个 [l,r] 的大线段,且该线段是最右的。于是此处就可以跟前面一样,枚举一个 k[l,r] 进行转移了。

时间复杂度 O(n3),因为枚举了 i,j,k 三个元素。

代码:

#include<bits/stdc++.h>
using namespace std;
#define bs(x) lower_bound(v.begin(),v.end(),x)-v.begin()+1
const int N=110;
int n,m,f[N][N];
pair<int,int> p[N];
vector<int>v;
int L[N],P[N],R[N],res;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
		scanf("%d%d",&p[i].first,&p[i].second);
		v.push_back(p[i].first);
		v.push_back(p[i].first-p[i].second);
		v.push_back(p[i].first+p[i].second);
	}
    sort(p+1,p+n+1);
	sort(v.begin(),v.end());
	v.resize(m=unique(v.begin(),v.end())-v.begin());
    for(int i=1;i<=n;i++){
		L[i]=bs(p[i].first-p[i].second);
		P[i]=bs(p[i].first);
		R[i]=bs(p[i].first+p[i].second);
	}
    for(int i=1;i<=n;i++){
        memcpy(f[i],f[i-1],sizeof(f[i]));
        for(int j=P[i];j<=R[i];j++) f[i][j]=max(f[i][j],f[i-1][P[i]]+v[j-1]-v[P[i]-1]);
        for(int j=i;j;j--){
            int Rmax=(j==i?P[j]:R[j]);
            if(Rmax<P[i])continue;
            int Lmin=(j==i?L[j]:P[j]);
            for(int k=j+1;k<=i;k++) Lmin=min(Lmin,L[k]);
            for(int k=Lmin;k<=Rmax;k++) f[i][k]=max(f[i][k],f[j-1][Lmin]+v[k-1]-v[Lmin-1]);
        }
        for(int j=1;j<=m;j++) f[i][j]=max(f[i][j],f[i][j-1]);
    }
    for(int i=1;i<=m;i++) res=max(res,f[n][i]);
    printf("%d\n",res);
    return 0;
}
posted @   xuantianhao  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示