题意:
给 N N N对区间,每对区间 [ p i − l i , p i ] [pi-li,pi] [pi−li,pi]和 [ p i , p i + r i ] [pi,pi+ri] [pi,pi+ri]两个里面只能选一个。求最大覆盖。
N ≤ 1 0 3 N\le10^3 N≤103。
离散化。按照 p i pi pi从小到大把区间排序。逐个更新。
记 F [ i ] [ j ] F[i][j] F[i][j]为更新第 i i i个区间后,从 P o s [ 1 ] Pos[1] Pos[1]到 P o s [ j ] Pos[j] Pos[j]的最大覆盖。
初始化 F [ i ] [ j ] = F [ i − 1 ] [ j ] F[i][j]=F[i-1][j] F[i][j]=F[i−1][j]。
选左边:
l a s = F [ i − 1 ] [ p i − l i ] las=F[i-1][pi-li] las=F[i−1][pi−li]。
j j j从 p i − l i + 1 pi-li+1 pi−li+1到 p i pi pi, F [ i ] [ j ] = m a x ( F [ i ] [ j ] , l a s + P o s [ j ] − P o s [ p i i − l i i ] ) F[i][j]=max(F[i][j],las+Pos[j]-Pos[pi_i-li_i]) F[i][j]=max(F[i][j],las+Pos[j]−Pos[pii−lii])。
直接覆盖即可。
至于右边会不会有哪个区间甚至覆盖到 p i − l i pi-li pi−li之前,
如果真的有,那这里就完全没有必要选择左边了,所以不管它。(反正真有的话之后也会被覆盖掉
选右边:
l a s = F [ i − 1 ] [ p i ] las=F[i-1][pi] las=F[i−1][pi]。
j j j从 p i + 1 pi+1 pi+1到 p i + r i pi+ri pi+ri, F [ i ] [ j ] = m a x ( F [ i ] [ j ] , l a s + P o s [ j ] − P o s [ p i i ] ) F[i][j]=max(F[i][j],las+Pos[j]-Pos[pi_i]) F[i][j]=max(F[i][j],las+Pos[j]−Pos[pii])。
i d [ j ] id[j] id[j]为当 j j j是某个 p i pi pi时,它所属于的区间对编号(离散化排序后)。
对于每个 j j j,要在更新 F [ i ] [ j ] F[i][j] F[i][j]后,
更新 l a s = m a x ( l a s , F [ i − 1 ] [ l i i d [ j ] ] + P o s [ p i i ] − P o s [ l i i d [ j ] ] ) las=max(las,F[i-1][li_{id[j]}]+Pos[pi_i]-Pos[li_{id[j]}]) las=max(las,F[i−1][liid[j]]+Pos[pii]−Pos[liid[j]])
右边和左边会有这样的差别,一个原因是选左边的时候,碰到的都是之前处理过的;而扫右边的时候遇到的区间都是将来才会处理的区间。
而且这些区间,如果它的左端点在 p i pi pi左边,同时又知道它的右端点在 p i + r i pi+ri pi+ri左边,那么这些区间和当前处理的右区间之间并不会有包含关系
选左边的,处理出来的 F F F已经是左区间最终可能产生的贡献了(被覆盖就是覆盖得彻彻底底)。
选右边的,如果不考虑将来这些和它相交的区间,处理出来的 F F F除了和选的右区间有关,将来还可能会被改变(因为 p i pi pi之前的部分可能被新区间覆盖)。但是以后再也不会选到这个右区间,如果现在因为左边状态不确定而选右区间比不过选左区间的话,也就没有机会考虑这个右区间贡献改变之后会不会更优了。所以要及时考虑上将来的贡献。
写得有点乱,可能自己理解式子容易一点(
最后要从左到右更新一轮最大覆盖。这样也就是敲定这里到底选左区间还是右区间了。
F [ i ] [ j ] = m a x { F [ i ] [ k ] } , k ∈ [ 1 , j − 1 ] F[i][j]=max\{F[i][k]\},k\in[1,j-1] F[i][j]=max{F[i][k]},k∈[1,j−1]
一开始拿到这道题满脑子 F [ N ] [ 2 ] F[N][2] F[N][2]…
基础的思路是拆开来考虑,按顺序选区间对,记录处理完某个区间对之后最优答案
而不要分开记录选左边和选右边的答案。这样在本题反而不好处理。
因为受到影响可能不只是来自于前一个区间对。影响的考虑应该是全局的
想到可以分别处理左区间答案和右区间答案,最后扫一遍就可以在末端得到本轮最大覆盖
然后再考虑两个区间分别要怎么搞。
具体思路上面已经说明了,在某种程度上有点像整体二分(不过也很不像
弱化
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define adpt(p,l,r) pt[++pt[0]]=p,pt[++pt[0]]=p-l,pt[++pt[0]]=p+r
int N;
struct sut
{
int pi,li,ri;
sut (int a=0,int b=0,int c=0) { pi=a; li=b; ri=c;}
bool operator < (const sut&od) const { return pi<od.pi;}
}st[3005];
int pt[9005]={},id[9005]={},F[3005][9005]={}; //可以滚动F[][]优化空间
int main()
{
scanf("%d",&N);
for(int i=1;i<=N;++i)
{
scanf("%d%d%d",&st[i].pi,&st[i].li,&st[i].ri);
adpt(st[i].pi,st[i].li,st[i].ri);
}
sort(st+1,st+1+N);
sort(pt+1,pt+1+pt[0]);
pt[0]=unique(pt+1,pt+1+pt[0])-pt-1;
for(int i=1;i<=N;++i)
{
st[i].li=lower_bound(pt+1,pt+1+pt[0],st[i].pi-st[i].li)-pt;
st[i].ri=lower_bound(pt+1,pt+1+pt[0],st[i].pi+st[i].ri)-pt;
st[i].pi=lower_bound(pt+1,pt+1+pt[0],st[i].pi)-pt; //
if((!id[st[i].pi])||(st[id[st[i].pi]].li>st[i].li))id[st[i].pi]=i;
}
for(int i=1;i<=N;++i)
{
for(int j=1;j<=pt[0];++j)F[i][j]=F[i-1][j];
for(int j=st[i].li+1;j<=st[i].pi;++j)F[i][j]=max(F[i][j],F[i-1][st[i].li]+pt[j]-pt[st[i].li]);
for(int las=F[i-1][st[i].pi],j=st[i].pi+1;j<=st[i].ri;++j)
{
F[i][j]=max(F[i][j],las+pt[j]-pt[st[i].pi]);
if(id[j]&&st[id[j]].li<st[i].pi)las=max(las,F[i-1][st[id[j]].li]+pt[st[i].pi]-pt[st[id[j]].li]);
}
for(int premax=F[i][1],j=2;j<=pt[0];++j)premax=max(premax,F[i][j]),F[i][j]=premax;
}
printf("%d",F[N][pt[0]]);
return 0;
}