zoukankan      html  css  js  c++  java
  • USACO07MAR Face The Right Way G 差分

    题目链接 https://www.luogu.com.cn/problem/P2882

    分析

    这个题来看的话好像有点难下手,不如再去读一遍题 N遍,发现一句话很重要Each time the machine is used, it reverses the facing direction of a contiguous group of K cows in the line,就是说只能翻转固定的长度区间,那这样是不是就可以枚举区间了?枚举一层区间,再枚举每次起点,最后加上区间修改,时间复杂度(O(N^3)),肯定会T掉,接下来就考虑优化了。
    优化怎么入手呢?时间主要就是出在这三层循环上,只要省掉一层循环,时间复杂度就能到(O(N^2)),这样就可以过,第一层循环,显然不能省略,第二层同样,只有在区间修改这一层循环上可以做点手脚,回忆区间修改,有几种做法,线段树,树状数组,还有差分,前两者用在这都有点大材小用或是说不是很合适,因为判断是否区间修改完成不好判断,而差分用在这个区间上就很合适了。那我们大概思路也就有了,首先读入数组,将B标记成1,F标记成0,这里怎么标记都无所谓,然后利用枚举区间,差分修改,最后输出答案,下面考虑一下细节。
    我们枚举区间完,要从左到右一次反转区间,为什么呢?题目中要求的是最小次数,就是要先保证次数最小,再考虑区间长度,而我们如果先修改后面的,把后面改好了,再去改前边的,结果一定不会比先改前边的好(有可能相等,如00100),所以我们为保证最小次数,一定从最左端开始依次枚举,如果这个点不符合,就把他后面的整个区间翻转,这里就要用到差分了,肯定直接修改会T掉,我们可以考虑,如果这个区间要修改,那么原来的1会变成2,0会变成1,好像没什么规律,但再看就发现所有的奇数都需要改变,偶数就不用,每次修改给整个区间加一,判断奇偶数就行,然后这就变成了一个区间加一个数的操作,相信大家应该都会。这样修改就完成了,那么怎么判断能不能完成题目的任务呢?由题意可以知道如果当前区间长度小于修改的区间长度,是不能修改的,也就是从n往前的长度为len的区间总是无法被修改的,所以判断这一段区间内有无不满足条件的点即可。
    最后找答案的时候也有一个地方,就是当操作修改次数不同时,直接用操作修改次数最小的那个答案就行,但如果当前操作次数和原来答案相同,是不是要考虑一下区间长度改成最小值?答案显然是不是,…………,因为我们是从小到大枚举的区间长度,所以在遇到相等的时候,已经得到的答案的区间长一定是小的,所以只在次数不同时修改答案,但判断上也不会错。

    其他优化

    当然以下优化不加也没问题,毕竟算法时间复杂度足够过掉这道题。
    我做完之后看了看时间大概700ms左右,好像有点高,看别人的时间好像没有特别大,所以我加了加小优化。

    为方便说,由上到下一次标号(1-4),1,2跑的时间还是挺快的但没啥用,(NOIp)不可能给你开O2也不可能给你c++17,所以还是看一下3和4,这俩时间大概有一倍的关系,看一下代码吧

    3

    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int N=5e3+10;
    char s[3];
    int cf[N],a[N];
    int min(int a,int b){
        if(a<b)return a;
        else return b;
    }
    int main(){
        int n;scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%s",s);
            if(s[0]=='B')a[i]=1;
            else a[i]=0;
        }
        int res=0x3f3f3f3f,ans=0x3f3f3f3f;
        for(int len=1;len<=n;len++){
            int cnt=1,k=0;memset(cf,0,sizeof(cf));
            for(int i=1;i<=n;i++){
                cf[i]+=cf[i-1];
                if(i+len-1<=n){
                    if(a[i]+cf[i]&1){
                        cf[i]++;cf[i+len]--;k++;
                    }
                }else if(cf[i]+a[i]&1){cnt=0;break;}
            }
            if(cnt)
                if(k<ans){
                    ans=k;res=len;
                }
                else if(k==ans)res=min(res,len);
        }
        printf("%d %d",res,ans);
        return 0;
    }
    
    

    4

    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int N=5e3+10;
    char s[3];
    int cf[N],a[N];
    int min(int a,int b){
        if(a<b)return a;
        else return b;
    }
    int main(){
        int n;scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%s",s);
            if(s[0]=='B')a[i]=1;
            else a[i]=0;
        }
        int res=0x3f3f3f3f,ans=0x3f3f3f3f;
        for(int len=1;len<=n;len++){
            int cnt=1,k=0;memset(cf,0,sizeof(cf));
            for(int i=1;i<=n;i++){
                cf[i]+=cf[i-1];
                if(i+len-1<=n){
                    if(a[i]+cf[i]&1){
                        cf[i]++;cf[i+len]--;k++;
                    }
                }else if(cf[i]+a[i]&1)cnt=0;
            }
            if(cnt)
                if(k<ans){
                    ans=k;res=len;
                }
                else if(k==ans)res=min(res,len);
        }
        printf("%d %d",res,ans);
        return 0;
    }
    
    

    其实就是少一个break,感觉这个加上还是很有必要的,因为可能极限数据的时候,CCF那评测机状态不好,再卡一下,可能会出问题。

    问题

    那么有没有可能最开始全部是朝前的呢?答案是没有,英文题面中已经讲到,有一些牛,所以说不可能其实全部朝前边的。

  • 相关阅读:
    leetcode Super Ugly Number
    leetcode Find Median from Data Stream
    leetcode Remove Invalid Parentheses
    leetcode Range Sum Query
    leetcode Range Sum Query
    leetcode Minimum Height Trees
    hdu 3836 Equivalent Sets
    hdu 1269 迷宫城堡
    hud 2586 How far away ?
    poj 1330 Nearest Common Ancestors
  • 原文地址:https://www.cnblogs.com/anyixing-fly/p/12640589.html
Copyright © 2011-2022 走看看