HDU 6365 Shoot Game

版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/81673809

题意:n(<300)条X轴之上且平行于X轴的线段,每条有个权值,只有你发射的射线的能量大于这个权值且射线与线段相交才能消除这个线段,求消除所有线段的最小的(能量值的和)。
一开始就想到先打最大权值线段,然后就贪心到无法自拔,然而DeadEnd
考虑DP,发现只瞄准线段端点打也可以得到最优解,发现可以把端点用斜率离散,那么我们就把一个二维计算几何问题离散成了一个一维序列问题:平面上有n条线段,每次打一个点,一共只有600个点。
然后这就是一个很显然的区间DP,DP(L,R)表示打完了左右端点都在L,R范围内的线段的最小花费。
转移就找L,R中每个端点作为一次发射射线的位置,
然后我们发现我们需要找L,R范围内包含点K的最大权值的线段,
这个,或许有什么奇技淫巧能做吧,但是我放弃。
发现区间(L,R)中最大权值的线段在只考虑(L,R)的时候是一定会被能量值等于其权值的射线击穿的(就算有多条权值最大的线段也如此),那么我们就枚举区间最大线段中的每个点就行了,一定会得到最优解,O(n)完成。

这个题,首先n^3可过的题就别幻想O(n)贪心了,其次区间DP之类转移十分特殊的DP大多数都是可以优化的,不要想到一个O(n^5)之类的DP就放弃,优化一下也能变成O(n)的(比如雅礼集训XXX),毕竟DP的优化种类也不是你一只手数的完的,况且一般都是一下少一个n之类的大杀器

AC Code:

#include<bits/stdc++.h>
#define maxn 605
#define LL long long
using namespace std;

template<class T>inline void read(T &res){ bool flag=0;char ch;for(;!isdigit(ch=getchar());)if(ch=='-')flag=1; for(res=ch-'0';isdigit(ch=getchar());res=res*10+ch-'0'); (flag) && (res = -res);}
int n,c[maxn];
LL W[maxn],dp[maxn][maxn];
double L[maxn],R[maxn],H[maxn],K[maxn];

inline bool cmp(const int &a,const int &b){ return K[a] < K[b]; }

int main()
{
    int T;
    for(read(T);T--;)
    {
        read(n);
        memset(dp,0x3f,sizeof dp);
        for(int i=1;i<=n;i++)
        {
            read(H[i]),read(L[i]),read(R[i]),read(W[i]);
            K[i<<1] = L[i] / H[i] , K[i<<1|1] = R[i] / H[i];
            c[i<<1] = i<<1 , c[i<<1|1] = i<<1|1;
        }
        sort(c+2,c+(n<<1|1)+1,cmp);
        for(int i=1;i<=n;i++)
            for(int j=2;j<=(n<<1|1);j++)
                if((L[i] / H[i] - K[c[j]]) < 1e-12)
                {
                    L[i] = j;
                    break;
                }
        for(int i=1;i<=n;i++)
            for(int j=2;j<=(n<<1|1);j++)
                if((R[i] / H[i] - K[c[j]]) < 1e-12)
                {
                    R[i] = j;
                    break;
                }

        for(int len=0,j;len<(n<<1|1);len++)
            for(int i=2;i+len<=(n<<1|1);i++)
            {
                j=i+len;
                int Maxloc=-1;
                for(int k=1;k<=n;k++)
                    if(L[k] >= i && R[k] <= j && (Maxloc == -1 || W[k] > W[Maxloc]) )
                        Maxloc = k;
                if(Maxloc == -1) dp[i][j] = 0;
                else
                    for(int k=L[Maxloc];k<=R[Maxloc];k++)
                        dp[i][j] = min(dp[i][j] , (k-1 >= i) * dp[i][k-1] + (k+1 <= j) * dp[k+1][j]+W[Maxloc]);
            }

        printf("%lld\n",dp[2][n<<1|1]);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_35950004/article/details/81673809