• CF939F Cutlet


    传送门

    题意

    有一块烤肉,\(2n+1\) 个时刻,\(2n\)个时间段,每一时间段只能烤一面烤肉, 要求正反两面分别\(n\)个时间, 给你\(k\)个无交区间,你可以在这些区间内给烤肉翻面,求最小翻面次数。\(n \leq 1e5, k \leq 100\)


    题解

    看到题感觉是一道带结论的dp,然后开始想,先证他一手假贪心,然后转化为这么一个模型,你要选出最少的区间长度相加为\(n\), 保证区间端点在可翻转区间内。然后再想结论,有没有这样这种可能:一个区间只能翻转1次,错的,然后有结论1:

    一个区间内最多翻转两次。我们翻转相当于取一些区间相加为\(n\), 你在一个可翻转区间里取两个答案区间, 为什么不合并成一个呢?所以只有两种情况,一个可翻转区间内取,翻转两次。 取的区间两个端点在不同可翻转区间,各翻转一次。

    这给我们一个启发,多个区间合并起来更优。假如最优答案有很多区间,这些区间并没有把它所属可翻转区间占满,那我们把他们尽可能合并起来显然不劣。所以有结论: 一定存在一种最优答案: 其最多存在一个可翻转区间只取了一部分, 其他都取了整个区间或没取
    读者可自证。

    现在不仅可翻转区间可以整个取, 不可翻转区间也可以整个取(读自证),然后有做法,枚举这个只去一部分的区间,称之为零碎区间。我们枚举所有可翻转区间, 将其作为零碎区间所在的那个区间, 然后它左边要取若干个区间,右边取若干个区间, 我们设len为我们当前枚举的区间的长度,实际上是零碎区间的最大长度。那么只要左右区间长度和在\([n-len, n]\)之间即可,剩下的可以用零碎区间补齐,费用与零碎区间的长度显然无关,而与零碎区间相邻的区间是否选取有关(如果选了的话,直接把相邻区间的端点移到零碎区间的端点即可,无花费)

    剩下的就暴力dp, 设\(f_{i, j}\)为前\(i\)个区间里选\(j\)长度需要多少区间, 再倒着dp一遍,然后枚举的时候用单调队列维护一手最小值即可。最近博客估计要大摆,不懂可看代码或问我(


    实现

    #include <iostream>
    #include <cstdio>
    using namespace std;
    
    int read(){
        int num=0, flag=1; char c=getchar();
        while(!isdigit(c) && c!='-') c=getchar();
        if(c == '-') c=getchar(), flag=-1;
        while(isdigit(c)) num=num*10+c-'0', c=getchar();
        return num*flag;
    }
    
    const int N = 1e5+50;
    const int M = 205;
    const int inf = 0x3f3f3f3f;
    
    int n, m, k;
    int ans = inf;
    int f[M][N][2], p[M][N][2], ls[N];
    int L[M], R[M], len[M], cnt=0, las=0, flag[M];
    
    int st[N], tim[N], sl=0, sr=0;
    
    int min(int a, int b){return a<b?a:b;}
    int max(int a, int b){return a>b?a:b;}
    
    int main(){
    	freopen("barbecue.in", "r", stdin);
    	freopen("barbecue.out", "w", stdout);
        n=read(), m=n*2, k=read();
        for(int i=1; i<=k; i++){
            cnt++;
            L[cnt]=R[cnt-1], R[cnt]=read(), len[cnt]=R[cnt]-L[cnt];
            cnt++;
            L[cnt]=R[cnt-1], R[cnt]=read(), len[cnt]=R[cnt]-L[cnt];
            flag[cnt]=1;
        }
        cnt++; L[cnt]=R[cnt-1], R[cnt]=m, len[cnt]=R[cnt]-L[cnt];
    
        for(int i=1; i<=n; i++) f[0][i][0] = f[0][i][1] = inf, p[cnt+1][i][0] = p[cnt+1][i][1] = inf;
        
        
        
        for(int i=0; i<=cnt+1; i++){
        	f[i][0][1]=1;
        	p[i][0][1]=1;
    	} 
    
        for(int i=1; i<=cnt; i++){
            for(int j=0; j<=n; j++){
                f[i][j][0] = min(f[i-1][j][0], f[i-1][j][1]);
                if(j-len[i] >= 0)
                    f[i][j][1] = min(f[i-1][j-len[i]][0] + 2, f[i-1][j-len[i]][1]);
                else f[i][j][1] = inf;
            }
        }
    
        for(int i=cnt; i; i--){
            for(int j=0; j<=n; j++){
                p[i][j][0] = min(p[i+1][j][0], p[i+1][j][1]);
                if(j-len[i] >= 0)
                    p[i][j][1] = min(p[i+1][j-len[i]][0]+2, p[i+1][j-len[i]][1]);
                else p[i][j][1] = inf;
            }
        }
    
        ans = min(min(f[cnt][n][0], f[cnt][n][1]), min(p[1][n][0], p[1][n][1]));
    
    
        for(int i=1; i<=cnt; i++){
        	if(!flag[i]) continue;
            int res = inf;
            int hav = len[i];
    
            int l=n+1, r=n+1;
    
            sl=1, sr=0;    
            for(int j=0; j<=n; j++){
                int cl=max(0, n-hav-j), cr=n-j;
                while(sl<=sr && tim[sl]>cr) sl++; 
                while(l > cl) {
                    l--;
                    int lp = min(p[i+1][l][0], p[i+1][l][1]);
                    while(sr>=sl && st[sr]>=lp) sr--;
                    st[++sr] = lp, tim[sr]=l;
                }
    
                res = min(res, min(f[i-1][j][0], f[i-1][j][1]) + 2 + st[sl]);
                res = min(res, f[i-1][j][1] + st[sl]);
            }
    
    
            l=n+1, r=n+1;
    
            sl=1, sr=0;    
            for(int j=0; j<=n; j++){
                int cl=max(0, n-hav-j), cr=n-j;
                while(sl<=sr && tim[sl]>cr) sl++; 
                while(l > cl) {
                    l--;
                    int lp = p[i+1][l][1];
                    while(sr>=sl && st[sr]>=lp) sr--;
                    st[++sr] = lp;
                }
    
                res = min(res, min(f[i-1][j][0], f[i-1][j][1]) + st[sl]);
            }
    
            ans = min(ans, res);
    
        }
    
    
        if(ans >= 2*n+100+m+n || ans >= inf-1000) printf("NO\n");
        else printf("YES\n%d\n", ans);
    
        return 0;
    }
    /***
    
    20 8
    4 4 
    9 9
    13 13
    18 18
    22 22
    27 27
    31 31
    36 36
    
    ****/
    
  • 相关阅读:
    jsp第六周作业
    jsp第四周作业
    jsp第一周周作业
    第一次软件测试课堂练习
    4.11jsp
    第六周作业
    第三周jsp作业
    3.10 jsp作业
    3.4软件测试
    JSP第六周作业
  • 原文地址:https://www.cnblogs.com/ltdjcoder/p/15917528.html
Copyright © 2020-2023  润新知