CF559E Gerald and Path
题面
有 \(n\) 条线段,每条线段给定其中一端的位置及长度,求所有线段覆盖的最大长度。
\(n \le 100\)。
题解
首先按端点位置升序排序,不难想出状态 \(f_{i,j,op}\) 表示考虑前 \(i\) 个点,定向后右端点最靠右的是 \(j\) 号点的线段,且定向方向为 \(op\) ( \(0\) 向左, \(1\) 向右) 的最大长度
记 \(R(pos,op)\) 表示点 \(pos\) 的线段定向为 \(op\) 的右端点
要从 \(f_{i,j,op}\) 转移到 \(f_{i+1,r,d}\) ,若 \(i+1\) 往右,则 \(f_{i+1,r,d} = f_{i,j,op} + min(len_{i+1},R(i+1,1)-R(j,op))\)
但若要往左,我们会发现很不好办,因为可能 覆盖了之前两个线段的空隙 ,而这个不好计算,我们只能设计状态为 \(R(j,op)\) 左边的区间都不再让贡献。此时发现, \(i+1\) 包含了靠右的那条线段,启发我们 从线段的四种关系来分析 。我们首先只能对 \(j\) 进行分析,然后推广出更普遍的方法。
-
若 \(R(j,op)\le R(i+1,op1)\) 且 \(L(j,op)\le L(i+1,op1)\) ,则就是上面的转移式
-
若 \(R(j,op)\ge R(i+1,op1)\) 且 \(L(j,op)\ge L(i+1,op1)\) ,则 \(min(len_{i+1},R(i+1,1)-R(j,op)\) 为负,相当于把右端点移到了 \(R(i+1,op1)\) ,再加上 \(R(r,d)-R(i+1,op1)\) (因为 \(r\) 在 \(i+1\) 左边,所以后面那段一定连续)。
-
若 \((j,op)\) 包含 \((i+1,op1)\) ,那么也是第二类情况的转移式
我们发现上面两个转移式其实可以合并,就是
\(f_{i+1,r,d} = f_{i,j,op} + min(len_{i+1},R(i+1,1)-R(j,op)) + R(r,d)-R(i+1,op1)\)
- 若 \((i+1,op1)\) 包含 \((j,op)\) ,那么 \((j,op)\) 没有贡献,可以直接忽略它,从前面的状态转移过来,转移式也是上面那个。而这个忽略显然可以往前推,即状态前推后可以把新的 \((j,op)\) 忽略掉,所以每次都是之前有过的状态。且忽略多了后答案会变小,所以直接对于所有 \(R(j,op)\le R(i+1,op1)\) 的状态都统计一下就好。
所以 \(R(j,op)\le R(i+1,op1)\) 和 \(R(j,op)\ge R(i+1,op1)\) 的转移式是一样的!
因为我们必须保证 \((j,op)\) 确实是最靠右的线段,所以采取贡献式,\(k\) 不断右移并且将忽略的线段贪心地向右定向,并维护 \(r,d\) 。
\(O(N^3)\)
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;
}