CF559E Gerald and Path

题面

n 条线段,每条线段给定其中一端的位置及长度,求所有线段覆盖的最大长度。
n100

题解

首先按端点位置升序排序,不难想出状态 fi,j,op 表示考虑前 i 个点,定向后右端点最靠右的是 j 号点的线段,且定向方向为 op ( 0 向左, 1 向右) 的最大长度

R(pos,op) 表示点 pos 的线段定向为 op 的右端点

要从 fi,j,op 转移到 fi+1,r,d ,若 i+1 往右,则 fi+1,r,d=fi,j,op+min(leni+1,R(i+1,1)R(j,op))

但若要往左,我们会发现很不好办,因为可能 覆盖了之前两个线段的空隙 ,而这个不好计算,我们只能设计状态为 R(j,op) 左边的区间都不再让贡献。此时发现, i+1 包含了靠右的那条线段,启发我们 从线段的四种关系来分析 。我们首先只能对 j 进行分析,然后推广出更普遍的方法。

  1. R(j,op)R(i+1,op1)L(j,op)L(i+1,op1) ,则就是上面的转移式

  2. R(j,op)R(i+1,op1)L(j,op)L(i+1,op1) ,则 min(leni+1,R(i+1,1)R(j,op) 为负,相当于把右端点移到了 R(i+1,op1) ,再加上 R(r,d)R(i+1,op1) (因为 ri+1 左边,所以后面那段一定连续)。

  3. (j,op) 包含 (i+1,op1) ,那么也是第二类情况的转移式

我们发现上面两个转移式其实可以合并,就是

fi+1,r,d=fi,j,op+min(leni+1,R(i+1,1)R(j,op))+R(r,d)R(i+1,op1)

  1. (i+1,op1) 包含 (j,op) ,那么 (j,op) 没有贡献,可以直接忽略它,从前面的状态转移过来,转移式也是上面那个。而这个忽略显然可以往前推,即状态前推后可以把新的 (j,op) 忽略掉,所以每次都是之前有过的状态。且忽略多了后答案会变小,所以直接对于所有 R(j,op)R(i+1,op1) 的状态都统计一下就好。

所以 R(j,op)R(i+1,op1)R(j,op)R(i+1,op1) 的转移式是一样的!

因为我们必须保证 (j,op) 确实是最靠右的线段,所以采取贡献式,k 不断右移并且将忽略的线段贪心地向右定向,并维护 r,d

O(N3)

Code

#include<bits/stdc++.h>
#define ll long long
#define ri register int
using namespace std;
const int maxn = 110;
ll f[maxn][maxn][2];
int n;
struct range{
	int a,len; friend bool operator < (range x,range y){return x.a < y.a;}
}ran[maxn];
inline int rd(){
    int res=0,f=0;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=1;
    for(;isdigit(ch);ch=getchar()) res=(res<<3)+(res<<1)+ch-48;
    return f?-res:res;
}
inline int R(int pos,int op){
	if(!pos) return -0x3f3f3f3f;
	return op ? ran[pos].a + ran[pos].len : ran[pos].a;
}
int main(){
	n = rd(); for(ri i = 1;i <= n;++i) ran[i].a = rd(),ran[i].len = rd();
	sort(ran + 1,ran + n + 1);
	memset(f,-1,sizeof(f));
	f[0][0][0] = f[0][0][1] = 0;
	for(ri i = 0;i < n;++i)
	    for(ri j = 0;j <= i;++j)
		for(ri op = 0;op <= 1;++op)
	 	    if(f[i][j][op] != -1){
			int r = j,d = op;

			for(ri k = i + 1;k <= n;++k){
   			    if(R(k,0) >= R(r,d)) r = k,d = 0;
				f[k][r][d] = max(f[k][r][d],f[i][j][op] + min(ran[k].len,R(k,0) - R(j,op)) + R(r,d) - R(k,0));
				if(R(k,1) >= R(r,d)) r = k,d = 1;
				f[k][r][d] = max(f[k][r][d],f[i][j][op] + min(ran[k].len,R(k,1) - R(j,op)) + R(r,d) - R(k,1));
			}
 		    }
	ll ans = f[n][n][1];
	for(ri i = 1;i <= n;++i) for(ri j = 0;j <= 1;++j) ans = max(ans,f[n][i][j]);
	printf("%lld\n",ans);
    return 0;
}
posted @   Lumos壹玖贰壹  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示