[Nowcoder] [牛客网NOIP赛前集训TG4C] 杀虫 [dp]

题意:
N N N对区间,每对区间 [ p i − l i , p i ] [pi-li,pi] [pili,pi] [ p i , p i + r i ] [pi,pi+ri] [pi,pi+ri]两个里面只能选一个。求最大覆盖。
N ≤ 1 0 3 N\le10^3 N103

离散化。按照 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[i1][j]

选左边:
l a s = F [ i − 1 ] [ p i − l i ] las=F[i-1][pi-li] las=F[i1][pili]
j j j p i − l i + 1 pi-li+1 pili+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[piilii])
直接覆盖即可。
至于右边会不会有哪个区间甚至覆盖到 p i − l i pi-li pili之前,
如果真的有,那这里就完全没有必要选择左边了,所以不管它。(反正真有的话之后也会被覆盖掉

选右边:
l a s = F [ i − 1 ] [ p i ] las=F[i-1][pi] las=F[i1][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[i1][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,j1]

一开始拿到这道题满脑子 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;
}

你可能感兴趣的:(dp,Nowcoder)