【HDU 6365】暑期多校day6 Shoot Game (区间dp)

题目大意

在二维平面的第一象限和第四象限上有 n 条线段表示 n 堵墙,每堵墙有一个坚固度 wi ,表示只有不小于 wi 的能量才能摧毁并贯穿它。你只能从原点向任意方向发射任意能量,问至少需要发射多少能量才能把所有的墙都摧毁。


解题思路

在考试的时候一直都想偏了,一直在考虑各个墙之间的遮挡关系,甚至画出了拓扑图。

由于这道题的线段都是在二维平面上,这就很烦,所以考虑降维。虽然发射能量的方向是连续的,但是离散为向各个线段的两端点发射能量就能包括所有的情况。所以通过对线段的端点进行极角排序就可以把二位平面上的线段降到一维去。

显然,对于一组相交的线段,摧毁它们的最小代价应该是最坚固的墙的坚固度。

考虑区间 dp ,其中 dp[i][j] 表示摧毁被区间 [i,j] 完全包含的线段,所需的最小能量值。每当合并两个区间时,先找到区间内坚固度最高的线段,因为如果要摧毁这个区间内的所有线段,发射的能量必定等于这条线段的坚固度。在坚固度最高的线段内枚举断点更新 dp 数组。转移如下:

dp[L][R]=min(dp[L,k1]+dp[k+1][R]+MaxWinrange[L,R]) d p [ L ] [ R ] = m i n ( d p [ L , k − 1 ] + d p [ k + 1 ] [ R ] + M a x W i n r a n g e [ L , R ] )


代码

#include <bits/stdc++.h>
using namespace std;

const int maxn=333;
typedef long long ll;
int n,tot;
int h[maxn], l[maxn], r[maxn], w[maxn];
ll dp[maxn*2][maxn*2];

struct vec {
    int x,y,id;
    vec() {}
    vec(int x_,int y_,int i):x(x_),y(y_),id(i) {}
    bool operator < (const vec &rhs) const {return 1ll*x*rhs.y-1ll*y*rhs.x<0;}
    bool operator == (const vec &rhs) const {return 1ll*x*rhs.y-1ll*y*rhs.x==0;}
}p[maxn*2];

void work() {
    scanf("%d",&n);
    tot=0;
    register int len,i,j,k;
    for(i=1;i<=n;++i) {
        scanf("%d%d%d%d",&h[i],&l[i],&r[i],&w[i]);
        p[++tot]=vec(l[i],h[i],i);
        p[++tot]=vec(r[i],h[i],i);
    }

    sort(p+1,p+1+tot);
    tot=unique(p+1,p+1+tot)-(p+1);
    for(i=1;i<=n;++i) {
        l[i]=lower_bound(p+1,p+1+tot,vec(l[i],h[i],i))-p;
        r[i]=lower_bound(p+1,p+1+tot,vec(r[i],h[i],i))-p;
    }

    for(len=1;len<=tot;++len)
    for(i=1;i+len-1<=tot;++i) {
        j=i+len-1;
        dp[i][j]=(1ll<<60);

        int x=0, y=0, maxx=-1;
        for(k=1;k<=n;++k) if(i<=l[k] && r[k]<=j && w[k]>maxx) {
            maxx=w[k];
            x=l[k], y=r[k];
        }
        if(maxx==-1) {dp[i][j]=0; continue;}
        for(k=x;k<=y;++k)
            dp[i][j]=min(dp[i][j],dp[i][k-1]+dp[k+1][j]+maxx);
    }
    printf("%I64d\n",dp[1][tot]);

    return;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("in","r",stdin);
#endif
    int T;
    for(scanf("%d",&T);T;T--)
        work();

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值