Description
在二维平面有 n n 个障碍物,第个障碍物为高度 Hi H i 处一段连续区间 [Li,Ri] [ L i , R i ] ,其防御值为 Wi W i ,从原点射击打掉这 N N 个障碍物,每次充能就可以打掉这条路径上所有防御值不超过 X X 的障碍物,问至少需要多少能量可以消除所有障碍物
Input
第一行一整数表示用例组数,每组用例首先输入一整数 n n 表示障碍物数量,最后行每行四个整数 Hi,Li,Ri,Hi H i , L i , R i , H i
(1≤T≤10,1≤n≤300,1≤Hi≤109,−109≤Li,Ri≤109,0≤Wi≤109) ( 1 ≤ T ≤ 10 , 1 ≤ n ≤ 300 , 1 ≤ H i ≤ 10 9 , − 10 9 ≤ L i , R i ≤ 10 9 , 0 ≤ W i ≤ 10 9 )
Output
输出消除所有障碍物所需的最小能量
Sample Input
2
3
1 1 2 2
2 -1 1 4
3 -2 -1 3
3
1 -1 1 2
2 -1 1 3
3 0 2 0
Sample Output
6
3
Solution
显然只用考虑从原点到端点的这 2n 2 n 条射线即可,如果要清除一个从原点开始的扇形区域,那么必然要处理掉防御值最大的那个障碍物,以此可以区间 DP D P 求解
将 2n 2 n 条射线按极角排序并离散化为 1 1 ~,以 dp[l][r] d p [ l ] [ r ] 表示处理掉第 l l 条射线与第条射线围成区域内所有障碍物所需要最小能量,假设该区间内障碍物的防御值最大为 w[p] w [ p ] ,该障碍物两端的编号为 L[p],R[p] L [ p ] , R [ p ] ,那么有转移
dp[l][r]=min(dp[l][r],dp[l][k−1]+dp[k+1][r]+w[p]),L[p]≤k≤R[p] d p [ l ] [ r ] = m i n ( d p [ l ] [ r ] , d p [ l ] [ k − 1 ] + d p [ k + 1 ] [ r ] + w [ p ] ) , L [ p ] ≤ k ≤ R [ p ]
时间复杂度 O(n3) O ( n 3 )
Code
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
#define maxn 605
struct node
{
int x,y;
node(int _x=0,int _y=0)
{
x=_x,y=_y;
}
bool operator<(const node&b)const
{
return 1ll*x*b.y<1ll*y*b.x;
}
bool operator==(const node&b)const
{
return 1ll*x*b.y==1ll*y*b.x;
}
}a[maxn];
int T,n,L[maxn],R[maxn],H[maxn],W[maxn];
ll dp[maxn][maxn];
ll dfs(int l,int r)
{
if(l>=r)return 0;
if(dp[l][r]!=1e18)return dp[l][r];
int p=0;
for(int i=1;i<=n;i++)
if(L[i]>=l&&R[i]<=r)
if(p==0||W[p]<W[i])p=i;
if(p==0)return dp[l][r]=0;
for(int k=L[p];k<=R[p];k++)
dp[l][r]=min(dp[l][r],dfs(l,k-1)+dfs(k+1,r)+W[p]);
return dp[l][r];
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
int m=0;
for(int i=1;i<=n;i++)
{
scanf("%d%d%d%d",&H[i],&L[i],&R[i],&W[i]);
a[m++]=node(L[i],H[i]),a[m++]=node(R[i],H[i]);
}
sort(a,a+m);
m=unique(a,a+m)-a;
for(int i=1;i<=n;i++)
{
L[i]=lower_bound(a,a+m,node(L[i],H[i]))-a+1;
R[i]=lower_bound(a,a+m,node(R[i],H[i]))-a+1;
}
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++)
dp[i][j]=1e18;
printf("%lld\n",dfs(1,m));
}
return 0;
}