CF559E Gerald and Path(DP)

CF559E Gerald and Path

\(dp(i,p)\) 表示完成前 \(i\) 条线段的覆盖,最右端位于 \(p\) 点的最大收益。

转移?向下一条线段转移时加上他们中间的距离?发现这样没有办法统计 \(p\) 点以前的空位了!

\(\color{yellow}{\bigstar\texttt{Trick}}\):如果出现上面没有办法统计 \(p\) 点以前的空位的情况,说明覆盖 \(p\) 点的这条线段是没有用的,每次只需要考虑有用的线段

若要枚举有用的线段,从 \(i\) 点枚举下一个有用的线段 \(k\),钦定 \([i+1,k-1]\) 之间的线段都是没有用的,这样就可以像上面一样转移了。

但是,如果没有用的线段中出现一个线段它的右端点超过了 \(k\) 线段的右端点呢?那就再扫过 \([i+1,k-1]\) 线段的时候统计下最右边端,再加上它与 \(k\) 线段右端的贡献即可。

实现的时候记 \(p\) 点只用记录最右端线段编号和朝向即可,设 \(dp(i,j,p)\) 表示到 \(i\) 线段为止,最右边的线段是 \(j\),它的朝向为 \(p\) 的最大收益。

\[dp(k,x,y)\leftarrow dp(i,j,p)+\min(r_k-p,len_k)+r_x-r_k \]

#define Maxn 105
int n,ans;
int dp[Maxn][Maxn][2];
struct Seg
{
	int pos,len;
	Seg(int Pos=0,int Len=0):pos(Pos),len(Len){}
	inline bool friend operator < (Seg x,Seg y) { return x.pos<y.pos; }
}s[Maxn];
int main()
{
	n=rd();
	for(int i=1,p,l;i<=n;i++) p=rd(),l=rd(),s[i]=Seg(p,l);
	sort(s+1,s+n+1),s[0].pos=-inf;
	for(int i=0;i<=n;i++)
		for(int j=0;j<=i;j++) for(int p=0;p<2;p++)
		{
			int rn=s[j].pos+p*s[j].len,maxx=-inf,pos=-1,d;
			ans=max(ans,dp[i][j][p]);
			for(int k=i+1;k<=n;k++) for(int q=0;q<2;q++)
			{
				int cur=s[k].pos+q*s[k].len;
				if(cur>=maxx) maxx=cur,pos=k,d=q;
				dp[k][pos][d]=max(dp[k][pos][d],
					dp[i][j][p]+min(cur-rn,s[k].len)+maxx-cur);
			}
		}
	printf("%d\n",ans);
	return 0;
}
posted @ 2022-08-14 17:45  EricQian06  阅读(25)  评论(0编辑  收藏  举报