• 1087. 【NOIP动态规划专题】鱼肉炸弹


    Description

    舒克和贝塔终于下定决心要去营救被关押在众猫聚居的 \(\mathrm A\) 城中的大米同志。

    \(\mathrm A\) 城的构造是很奇怪的。 \(\mathrm A\) 城中的所有 \(\mathrm N\) 栋建筑沿着一条直线排列,而且没有两栋楼的高度是相同的。而大米同志就被关押在其中的某栋建筑中。每一栋建筑的顶上都是有一些猫们在看守的。如果按照从一端到另一端的顺序将所有的建筑编号为 1 到 \(\mathrm{N}\), 那么第 \(\mathrm{i}\) 栋建筑的高度为 \(\mathrm{Hi}\),0顶上开始时的猫的数量为 \(\mathrm{Ci}\)

    每一只猫不但可以看守住其所在建筑的楼顶,还可以看守住一些比它所在建筑要低的楼的楼顶。前提是没有被其他楼所挡住。A 城中的建筑都是很高的,高到可以忽略它们之间的距离和它们的水平面面积。于是可以认为,楼 \(\mathrm{i}\) 上的猫能看守楼 \(\mathrm{j}\) 的楼顶,当且仅当楼 \(\mathrm{i}\) 的高度不低于楼 \(\mathrm{j}\),且楼 \(\mathrm{i}\) 到楼 \(\mathrm{j}\) 之间的所有楼房的高度都低于楼 \(\mathrm{i}\)
    现在, 神勇的贝塔同学已经潜入了 \(\mathrm A\) 城内部营救大米同志,而舒克则负责驾驶直升机提供空中支援。按照约定,贝克找到并救出大米后会爬上楼顶施放信号让舒克前来接应。

    舒克的飞机上装备有 \(\mathrm{K}\) 枚鱼肉炸弹。每一枚鱼肉炸弹都可以在被投放到某一座楼的楼顶一段时间之后使该楼所有猫丧失行动能力。由于舒克并不知道贝塔会登上哪座楼的楼顶, 所以现在他决定减少在最坏情况下与贝塔汇合时被发现的可能。具体来说,假设第 \(\mathrm{i}\) 栋楼被 \(\mathrm{Si}\) 只猫看守(注意 \(\mathrm{Si}\) 只猫包括在该楼上的 \(\mathrm{Ci}\) 只以及在其他楼上所有能看守该楼顶的猫),他希望便用这 \(\mathrm{K}\) 枚炸弹使得 \(\mathrm{Si}\) 中的最大值最小。聪明的舒克很快就通过正确地选择炸弹投放地点完成了这一目标。你能吗?

    Solution

    题目有点长,来找一些关键信息:

    没有两栋楼的高度是相同的。

    因为最后要求最大值最小,我们会想二分,但二分完后我们没有什么好的方法来判断二分的答案是否合法,即使用 \(\mathrm{dp}\) 也会存在后效性。

    因此我们想有没有一种 \(\mathrm{dp}\) 不存在后效性。

    我们注意到题目中说没有两栋楼的高度是相同的,因此一个区间的最大值有且仅有一个。如果我们按照最大值的顺序岂不是没有后效性。

    另外我们发现,通过最大值分成的左右两边是互不影响的,因此我们想到建树。

    每次取出当前区间最大值作为根,分别向左边和右边递归来做,左右儿子是左右区间的最大值。

    另外我们发现 \(K\) 最大只有 5,而这题又不可能状压,因此考虑将其设入状态。

    \(f_{x,i}\) 表示当前节点为 \(x\),用了 \(i\) 枚炸弹的答案。

    另外再设 \(l,r\) 分别表示左右儿子。

    转移的话就枚举 \(i,j\) 表示左边和右边各放多少个炸弹,\(f_{x,i+j}=\min (\max(f_{l,i},f_{r,j})+c_x)\)

    并且我们可以再来个炸弹将 \(x\) 也炸掉,\(f_{x,i+j+1}=\min (\max(f_{l,i},f_{r,j}))\)(注意保证 \(i+j+1\le K\))。

    最后答案就是 \(f_{rt,K}\)

    Code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 100005
    #define K 15
    #define ll long long
    using namespace std;
    ll n,k,rt,h[N],c[N],lson[N],rson[N],f[N][K];
    bool bj[N];
    int build(int l,int r)
    {
        if (l==r) return l;
        int x=0,y;
        for (int i=l;i<=r;++i)
            if (x<h[i]) x=h[i],y=i;
        if (y>l) lson[y]=build(l,y-1);
        if (y<r) rson[y]=build(y+1,r);
        return y;
    }
    void dfs(int now)
    { 
        bj[now]=true;
        if (!bj[lson[now]]) dfs(lson[now]);
        if (!bj[rson[now]]) dfs(rson[now]);
        for (int i=0;i<=k;++i)
            for (int j=0;i+j<=k;++j)
            {
                f[now][i+j]=min(f[now][i+j],max(f[lson[now]][i],f[rson[now]][j])+c[now]);
                if (i+j+1<=k) f[now][i+j+1]=min(f[now][i+j+1],max(f[lson[now]][i],f[rson[now]][j]));
            }
    }
    int main()
    {
        scanf("%lld%lld",&n,&k);
        for (int i=1;i<=n;++i)
            scanf("%d%d",&h[i],&c[i]);
        memset(f,0x3f3f3f3f,sizeof(f));
        f[0][0]=0;
        rt=build(1,n);
        dfs(rt);
        printf("%lld\n",f[rt][k]);
        return 0;
    }
    
  • 相关阅读:
    贴图UV动画
    编辑器开发读取LIGHTMAP的脚本
    一个角色旋转身体在向前行走的代码
    第一周
    《大道至简》读后感
    第二周
    Easyui,好的设计思路
    有关反射
    Easyui表格的行编辑
    冒泡排序
  • 原文地址:https://www.cnblogs.com/Livingston/p/15831650.html
Copyright © 2020-2023  润新知