zoukankan      html  css  js  c++  java
  • CF797F Mice and Holes 贪心、栈维护DP

    传送门


    首先(sum c)有些大,考虑将其缩小降低难度

    考虑一个贪心:第一次所有老鼠都进入其左边第一个容量未满的洞(如果左边没有就进入右边第一个未满的洞),第二次所有老鼠都进入其右边第一个容量未满的洞(如果右边没有就进入左边第一个未满的洞),我们只保留这(2N)个洞,答案也不会变,因为在最优情况下老鼠最多只会进入这些洞。通过这一步,我们的(sum c)降低到了(2N)级别。

    考虑将容量为(c)的洞拆分为(c)个容量为(1)的洞,将老鼠和洞放在一起按照(dis)即位置从小到大排序。然后不难想到一个(DP):设(f_{i,j})表示已经考虑前(i)个物体,其中老鼠的数量减去强制选定需要容纳一只老鼠的洞的数量为(j)时的最小距离,转移分当前为老鼠还是洞。

    但是如果直接转移很难找到优化点,考虑简化DP过程。

    如果当前是一只老鼠,那么我们的转移是固定的:(j geq 0)(f_{i,j} - dis_i ightarrow f_{i+1,j+1})(j<0)(f_{i,j} + dis_i ightarrow f_{i+1,j+1})

    如果当前是一个洞,对于(j>0)的情况,因为之前的老鼠一定要往右边走,而当前的洞是右边最近的一个,所以它一定会容纳一只老鼠,也就是(f_{i,j} + dis_i ightarrow f_{i+1,j-1})

    对于(j<0)的情况,之前有若干个洞还没有容纳老鼠,那么右边出现的老鼠一定会往左走,而这一个洞比它左边的洞相比于右边的老鼠更近,所以如果不选这一个洞,可以不选之前的某一个洞而改选这一个洞获得更优策略。这意味着当(j<0)是这一个洞是必须要选的,转移会是(f_{i,j} - dis_i ightarrow f_{i+1,j-1})

    那么唯独需要决策的只有(j=0)且当前是一个洞的情况,你可以选择这一个洞不容纳老鼠,那么会有一个特殊的转移:(f_{i+1,0} = min{f_{i , 0} , f_{i , 1} + dis_i})

    发现除了(f_{i,0})的转移以外其他的转移都是平移数组和统一加上减去一个值的形式。我们通过两个栈维护(DP)数组,一个维护(j>0),一个维护(j<0),在栈外部打上整体加法标记,即可(O(n))维护(DP)

    #include<bits/stdc++.h>
    //This code is written by Itst
    using namespace std;
    
    inline int read(){
        int a = 0;
        char c = getchar();
        bool f = 0;
        while(!isdigit(c)){
            if(c == '-')
                f = 1;
            c = getchar();
        }
        while(isdigit(c)){
            a = a * 10 + c - 48;
            c = getchar();
        }
        return f ? -a : a;
    }
    
    const int MAXN = 1e5 + 7;
    stack < long long > dp1 , dp2;
    struct stg{
        int a , b;
        bool operator <(const stg t)const{
            return a < t.a;
        }
    }now[MAXN];
    int mice[MAXN] , hole[MAXN][2] , times[MAXN][2] , dp[MAXN * 3][2];
    int N , M , cnt;
    long long sum , tag1 , tag2 , dp0;
    
    int main(){
        N = read();
        M = read();
        for(int i = 1 ; i <= N ; ++i)
            mice[i] = read();
        sort(mice + 1 , mice + N + 1);
        for(int i = 1 ; i <= M ; ++i){
            now[i].a = read();
            now[i].b = read();
            sum += now[i].b;
        }
        sort(now + 1 , now + M + 1);
        for(int i = 1 ; i <= M ; ++i){
            hole[i][0] = now[i].a;
            hole[i][1] = now[i].b;
        }
        if(sum < N){
            puts("-1");
            return 0;
        }
        	int p1 = 1 , p2 = 1;
        while(p1 <= N || p2 <= M)
            if(p1 <= N && (p2 > M || hole[p2][0] > mice[p1])){
                ++p1;
                if(!dp1.empty()){
                    int t = dp1.top();
                    dp1.pop();
                    if(++times[t][0] != hole[t][1])
                        dp1.push(t);
                }
                else
                    ++cnt;
            }
            else
                if(cnt < hole[p2][1]){
                    times[p2][0] = cnt;
                    cnt = 0;
                    dp1.push(p2++);
                }
                else{
                    times[p2][0] = hole[p2][1];
                    cnt -= hole[p2++][1];
                }
        p1 = N;
        p2 = M;
        cnt = 0;
        dp1 = dp2;
        while(p1 || p2)
            if(p1 && (!p2 || hole[p2][0] < mice[p1])){
                --p1;
                if(!dp1.empty()){
                    int t = dp1.top();
                    dp1.pop();
                    if(++times[t][1] != hole[t][1])
                        dp1.push(t);
                }
                else
                    ++cnt;
            }
            else
                if(cnt < hole[p2][1]){
                    times[p2][1] = cnt;
                    cnt = 0;
                    dp1.push(p2--);
                }
                else{
                    times[p2][1] = hole[p2][1];
                    cnt -= hole[p2--][1];
                }
        p1 = 1;
        p2 = 1;
        cnt = 0;
        while(p1 <= N || p2 <= M)
            if(p1 <= N && (p2 > M || hole[p2][0] > mice[p1]))
                dp[++cnt][0] = mice[p1++];
            else{
                for(int i = min(times[p2][0] + times[p2][1] , hole[p2][1]) ; i ; --i){
                    dp[++cnt][0] = hole[p2][0];
                    dp[cnt][1] = 1;
                    --times[p2][0];
                }
                ++p2;
            }
        dp1 = dp2;
        for(int i = 1 ; i <= cnt ; ++i)
            if(!dp[i][1]){
                dp1.push(dp0 - tag1);
                tag1 -= dp[i][0];
                tag2 += dp[i][0];
                if(!dp2.empty()){
                    dp0 = dp2.top() + tag2;
                    dp2.pop();
                }
                else
                    dp0 = 1e15;
            }
            else{
                dp2.push(dp0 - tag2);
                tag1 += dp[i][0];
                tag2 -= dp[i][0];
                if(!dp1.empty()){
                    dp0 = min(dp0 , dp1.top() + tag1);
                    dp1.pop();
                }
            }
        cout << dp0;
        return 0;
    }
    
  • 相关阅读:
    spring与springmvc父子容器
    spring容器BeanFactory简单例子
    spring整体架构
    css中".",",",“~”和“>”符号的意义
    CSS中的块级元素与行级元素
    java反射和动态代理
    thymeleaf的fragment例子
    编写一个简单的 JDBC 程序
    http://localhost/ 或 http://127.0.0.1/ 报错:HTTP 404 的解决办法
    教你如何清除 MyEclipse/Eclipse 中 Web Browser 和 Switch Workspace 的历史记录
  • 原文地址:https://www.cnblogs.com/Itst/p/10271244.html
Copyright © 2011-2022 走看看