比较简单的题。
如果尝试直接DP每个灯可能比较困难,不妨反过来想,考虑给定若干个不相交区间,判定是否能完全覆盖。离散化后只有
O
(
n
2
)
\mathcal O(n^2)
O(n2)个有效的区间,用一个简单的DP就可以预处理出每个区间是否可以完全覆盖了,即我们固定左端点
s
s
s,枚举右端点,维护
F
[
i
]
[
j
]
F[i][j]
F[i][j]表示当前考虑到的右端点为
i
i
i,只用了
[
s
,
i
]
[s,i]
[s,i]中的灯,
[
s
,
i
]
[s,i]
[s,i]中还没有被覆盖的最左边位置为
j
j
j所能覆盖的最远位置,转移很简单。
最后再跑个DP求出答案就好了,时间复杂度
O
(
n
3
)
\mathcal O(n^3)
O(n3)。似乎有神仙贪心,想不动了。
#include <bits/stdc++.h>
#define FR first
#define SE second
using namespace std;
typedef pair<int,int> pr;
inline void update(int &x,int y) {
x=max(x,y);
}
int f[305],val[305];
pr num[305];
bool can[305];
int g[305][305],id[305];
void dp1(int n,int s) {
memset(g,255,sizeof(g));
memset(can,0,sizeof(can));
g[s-1][s-1]=s-1;
for(int i=s;i<=n;i++) {
if (id[i]) {
int l=num[id[i]].FR,r=num[id[i]].SE;
for(int j=s-1;j<i;j++)
if (g[i-1][j]!=-1) {
update(g[i][(l<=j)?s-1:j],max(g[i-1][j],i));
update(g[i][(j==s-1&&g[i-1][j]<i)?i-1:j],max(g[i-1][j],r));
}
}
else {
for(int j=s-1;j<i;j++)
if (g[i-1][j]!=-1) update(g[i][(j==s-1&&g[i-1][j]<i)?i-1:j],g[i-1][j]);
}
if (g[i][s-1]>=i) can[i]=1;
}
}
void dp2(int n) {
for(int i=n;i>0;i--) {
f[i]=f[i+1];
dp1(n,i);
for(int j=i+1;j<=n;j++)
if (can[j]) update(f[i],f[j+1]+val[j]-val[i]);
}
}
int main() {
int n;
scanf("%d",&n);
int cnt=0;
for(int i=1;i<=n;i++) {
int x,y;
scanf("%d%d",&x,&y);
num[i]=pr(x,y);
val[++cnt]=x-y;
val[++cnt]=x;
val[++cnt]=x+y;
}
sort(val+1,val+cnt+1);
cnt=unique(val+1,val+cnt+1)-val-1;
for(int i=1;i<=n;i++) {
int u=lower_bound(val+1,val+cnt+1,num[i].FR)-val;
int t1=lower_bound(val+1,val+cnt+1,num[i].FR-num[i].SE)-val;
int t2=lower_bound(val+1,val+cnt+1,num[i].FR+num[i].SE)-val;
id[u]=i;
num[i]=pr(t1,t2);
}
dp2(cnt);
printf("%d\n",f[1]);
return 0;
}