题意:给了一堆平行于x轴的线段,这些线段有一个护甲值,现在原点射激光,激光不能穿过护甲值高于激光强度的线段,每次射出激光花费的代价是激光强度,求射穿所有激光花费的最小代价
大致思路:挺精典的区间dp,比赛时一直考虑激光射穿之后这段区间会对后面造成什么影响,其实不用考虑这么麻烦。我们把所有的点离散化之后,对于每段区间,我们找到完全包含在内的线段,取护甲值最大的那一段,将其打穿,把大区间分成两块。还是一个比较直观的dp的。
#include<bits/stdc++.h>
using namespace std;
const int N = 666;
typedef long long ll;
struct point {
int x, y;
point (int x = 0, int y = 0) :x(x), y(y) {}
bool operator < (const point &rhs) const {
return (1ll * x * rhs.y - 1ll * y * rhs.x) < 0;
}
bool operator == (const point &rhs) const {
return (1ll * x * rhs.y - 1ll * y * rhs.x) == 0;
}
}ang[N];
struct edge {
point l, r;
int w;
}e[N];
int t, n, cnt, l[N], r[N];
ll dp[N][N];
int main() {
scanf("%d", &t);
while (t--) {
cnt = 0;
scanf("%d", &n);
for (int i = 0, h, l, r, w; i < n; i++) {
scanf("%d%d%d%d", &h, &l, &r, &w);
e[i].l = ang[cnt++] = point(l, h);
e[i].r = ang[cnt++] = point(r, h);
e[i].w = w;
}
sort(ang, ang + cnt);
int sz = unique(ang, ang + cnt) - ang;
for (int i = 0; i < n; i++) {
l[i] = lower_bound(ang, ang + sz, e[i].l) - ang;
r[i] = lower_bound(ang, ang + sz, e[i].r) - ang;
}
memset(dp, 0x3f, sizeof dp);
for (int i = 0; i <= sz; i++) dp[i + 1][i] = 0;
for (int i = sz - 1; i >= 0; i--)
for (int j = i; j < sz; j++) {
int mx = -1, id;
for (int tmp = 0; tmp < n; tmp++) if (e[tmp].w > mx && i <= l[tmp] && r[tmp] <= j) {
mx = e[tmp].w;
id = tmp;
}
if (mx == -1) dp[i][j] = 0;
else for (int k = l[id]; k <= r[id]; k++) dp[i][j] = min(dp[i][j], dp[i][k - 1] + dp[k + 1][j] + mx);
}
printf("%lld\n", dp[0][sz - 1]);
}
}