【洛谷4766】[CERC2014] Outer space invaders(区间DP)

点此看题面

  • 有\(n\)个区间,每个区间有一个权值\(d_i\)。
  • 一次操作可以选中一个点,花费\(D\)的代价,消灭所有包含这个点且\(d_i\le D\)的区间。
  • 求消灭所有区间的最小代价。
  • \(n\le300\)

区间\(DP\)

对于这种问题,容易想到按权值从大到小考虑每个区间,这样一来我们选中一个点就可以确保删去当前剩余的所有包含这个点的区间。

于是我们设\(f_{l,r}\)表示删去所有完全包含在\([l,r]\)内的区间的最小代价,并预处理出\(p_{l,r}\)表示完全包含在\([l,r]\)内的权值最大的区间编号。

那么对于\(f_{l,r}\),我们只需在第\(p_{l,r}\)个区间中枚举选中点\(i\),则所有包含\(i\)的区间都将被删去,因此只要从\(d_{p_{l,r}}+f_{l,i-1}+f_{i+1,r}\)转移即可。

代码:\(O(n^3)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 300
using namespace std;
int n,a[N+5],b[N+5],d[N+5],dc,dv[2*N+5],p[2*N+5][2*N+5];
int f[2*N+5][2*N+5];I int DP(CI l,CI r)//区间DP求出f
{
	if(!p[l][r]) return 0;if(~f[l][r]) return f[l][r];//不存在区间;记忆化
	RI i,t=1e9;for(i=a[p[l][r]];i<=b[p[l][r]];++i) t=min(t,d[p[l][r]]+DP(l,i-1)+DP(i+1,r));return f[l][r]=t;//在权值最大的区间中枚举选中点
}
int main()
{
	RI Tt,i,j,l;scanf("%d",&Tt);W(Tt--)
	{
		for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d%d%d",a+i,b+i,d+i),dv[i]=a[i],dv[n+i]=b[i];
		for(sort(dv+1,dv+2*n+1),dc=unique(dv+1,dv+2*n+1)-dv-1,i=1;i<=dc;++i) for(j=1;j<=dc;++j) p[i][j]=0,f[i][j]=-1;//清空
		#define F5(x,y) (d[x]<d[y]&&(x=y))
		#define GV(x) (lower_bound(dv+1,dv+dc+1,x)-dv)//离散化
		for(i=1;i<=n;++i) a[i]=GV(a[i]),b[i]=GV(b[i]),F5(p[a[i]][b[i]],i);//根据每个区间给p赋初值
		for(l=2;l<=dc;++l) for(i=1,j=l;j<=dc;++i,++j) F5(p[i][j],p[i+1][j]),F5(p[i][j],p[i][j-1]);//区间DP预处理p
		printf("%d\n",DP(1,dc));
	}return 0;
}
上一篇:90%的开发都没搞懂的CI和CD!


下一篇:DBoW2 cmake代码注释