• HDU 3669 Cross the Wall


    题目大意

    给定 $N$ 个矩形的宽和高, $(h_1, w_1), (h_2, w_2), dots, (h_n w_n)$ . 现需要确定 $k$ ($k le K$, $K$ 给定) 个矩形 $(H_i, W_i)$ , 使得 $forall i (1le ile N), exists j, s.t. H_j ge h_i$ 且 $W_j ge w_i$, 此时称这后 $k$ 个矩形覆盖前 $N$ 个矩形, 代价为 $sum_{1le i le k} H_i W_i$ , 即 $k$ 个矩形的面积之和. 求覆盖这 $N$ 个矩形的最小代价.

    Solution

    DP方程

    对 $N$ 个矩形按 (宽, 高) 双关键字从小到大排序. 按此顺序构造一个高严格递减的矩形队列, 只考虑这个队列里的矩形, 记队列里的剩下的矩形数目为 $n, (n le N)$ .

    $dp[i][j]$ : 用 $j$ 个矩形覆盖队列中前 $i$ 个矩形的最小代价.

    $$
    egin{equation}
    dp[i][j] = min {dp[i'][j-1]+w_i imes h_{i'+1}} quad (0le i' <i) label{dp_eq}
    end{equation}
    $$

    边界条件: $dp[0][0]=0$

    斜率优化

    固定 $j$ , 用斜率优化 $i$ 这一维的决策.

    关于斜率优化的细致讨论见 this post.

    写出斜率不等式:
    $$
    egin{equation}
    g(i_1, i_2) < f(i) quad (i_1<i_2) label{general_ineq}
    end{equation}
    $$
    其中
    $$ g(i_1, i_2) = dfrac{dp[i_2][j-1] - dp[i_1][j-1]}{h[i_1+1]-h[i_2+1]}$$
    $$ f(i) = w[i]$$

    维护一个相邻点斜率严格递增的点的队列, 由于 $f(i)$ 单调递增 , 在维护单调队列的基础上, 计算 $dp[i][j]$ 之前, 若队首两点 $i_1, i_2$ 满足 $g(i_1, i_2) le f(i)$ , 就将队首元素出队, 重复该过程. 将队首元素代入 $ ef{dp_eq}$ 式右边计算 $dp[i][j]$ 的值.

    Implementation

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=5e4+5, K=105;
    using ll=long long;
    
    ll dp[N][K];
    
    // dp[i][j]=min()
    int n, k;
    ll w[N], h[N];
    
    bool cmp(int x, int y){
        return w[x]<w[y] || w[x]==w[y] && h[x]<h[y];    // error-prone
    }
    
    int idx[N];
    int q[N], t; // q[0]=0
    
    void prep(){
        for(int i=1; i<=n; i++)
            idx[i]=i;
        sort(idx+1, idx+n+1, cmp);
        t=0;
        // q[++tail]=idx[1];
        for(int i=1; i<=n; i++){
            while(t && h[q[t]]<=h[idx[i]])
                --t;
            q[++t]=idx[i];
        }
    }
    
    // x < y: index of q
    
    ll nu(int i, int x, int y){
        return dp[y][i]-dp[x][i];
    }
    
    ll de(int x, int y){
        return h[q[x+1]]-h[q[y+1]];
    }
    
    // the first dimension of dp[i][j] and q[i] are associated.
    
    int que[N], head, tail;
    
    int main(){
        h[0]=1e6+5;
        for(; cin>>n>>k; ){
            for(int i=1; i<=n; i++)
                scanf("%lld%lld", w+i, h+i);
    
            prep();
    
            for(int j=0; j<=k; j++)
                dp[0][j]=0;
            for(int i=1; i<=t; i++)
                dp[i][0]=1e12+5; //error-prone
    
            // p_i: (h[q[i+1]], dp[i])
            for(int j=1; j<=k; j++){    // j: number of rectangles.
                head=tail=0;    // index of que
                // 1. idx of q stored 2. 0-indexed
                que[tail++]=0;
                for(int i=1; i<=t; i++){    // i: index of q
                    while(tail-head>=2 &&
                        nu(j-1, que[head], que[head+1]) <= de(que[head], que[head+1])*w[q[i]])
                        ++head;
    
                    dp[i][j]=dp[que[head]][j-1]+h[q[que[head]+1]]*w[q[i]];
                    // printf("(%d,%d):%lld
    ", i, j, dp[i][j]);
    
                    while(tail-head>=2 &&
                        nu(j-1, que[tail-2], que[tail-1])*de(que[tail-1], i)
                        >=nu(j-1, que[tail-1], i)*de(que[tail-2], que[tail-1]))
                        --tail;
    
                    que[tail++]=i;  //error-prone
                }
            }
    
            ll res=LLONG_MAX;
            for(int j=1; j<=k; j++)
                res=min(res, dp[t][j]);
            assert(res>0);
            cout<<res<<endl;
        }
        return 0;
    }
    

    Pitfalls

    如何将已经能被其他矩形覆盖的矩形剔除.

  • 相关阅读:
    判断php变量是否定义,是否为空
    HTTP Client 编写
    推荐《冒号课堂——编程范式与OOP思想》
    一些免费的HTML编辑器
    如何判断mysql中数据表中两个列之间的相同记录和不同记录
    PostgreSQL 8.4, SQL Server 2008, MySQL 5.1比较
    JDBC纵览
    使用jdbc连接sql数据库
    关于PHP中变量的判定
    如何判断数据库中是否存在一个数据表
  • 原文地址:https://www.cnblogs.com/Patt/p/6247306.html
Copyright © 2020-2023  润新知