zoukankan      html  css  js  c++  java
  • Educational Codeforces Round 61 (Rated for Div. 2)-C. Painting the Fence 前缀和优化

    题意就是给出多个区间,要求去掉两个区间,使得剩下的区间覆盖范围最大。 

    当然比赛的时候还是没能做出来,不得不佩服大佬的各种姿势。

    当时我想的是用线段树维护区间和,然后用单点判0,维护区间间断个数。然后打到一半,就发现想法有问题。

     这道题正解就是简单的前缀和,或者DP。

     我为了更加深入理解,两种方法都试了试。

     前缀和版本:

     由于题目给的范围是5000,明显支持N^2,于是我们枚举去掉的两个,刚好满足,那么要如何才能O(1)的得到答案?

    我们其实可以这样,我们知道它的总覆盖的数目,这是非常容易求出的。减去枚举的区间对答案的贡献即可。那么如何减去贡献,我们知道两个区间,可能有两种情况,一种是有交集部分,这一部分需要在交集区间上减去2次,而剩下的部分需要在剩下部分减去1次就行。

    那么如果要对答案产生影响,答案就是

    总覆盖区间=枚举区间交集部分刚好被覆盖两次+剩下交集为空的区间被覆盖一次的个数

    考虑如何求被覆盖一次的个数,通过维护差分,求得每个点的被覆盖次数

    枚举区间,就能求得每个区间,被覆盖次数为1的个数(不会有交集)。

    那么如何求两个区间交集部分刚好被覆盖两次的值呢???

    很简单,由于这个区间交集是不确定的。那么我们如何求呢?

    我们已经知道每个点的覆盖次数。

    那么我们可以知道刚好被覆盖两次的点,所在位置。

    利用前缀和,和差分的思想,我们可以用一个新的前缀和数组,记录当前被覆盖的次数刚好被覆盖次数为1的位置。

    然后求前缀和,这样我们就能快速的求出,[L,R]区间内,刚好为2的个数。

    这样答案也出来了,注意枚举区间可能交集可能不存在,这样就不用算覆盖次数为2的个数。

    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #include<math.h>
    using namespace std;
    const int maxx = 5005;
    int pre[4][maxx];
    struct node{
      int l,r;
    }a[maxx];
    int main(){
      int n,q,sum,ans;
      while(~scanf("%d%d",&n,&q)){
        ans=0;
        sum=0;
        memset(pre,0,sizeof(pre));
        for (int i=1;i<=q;i++){
            scanf("%d%d",&a[i].l,&a[i].r);
            pre[0][a[i].l]+=1;
            pre[0][a[i].r+1]-=1;
        }
        for (int i=1;i<=n;i++){
            pre[0][i]+=pre[0][i-1];//每个位置的情况
            if (pre[0][i])sum++;
        }
        for (int i=1;i<=n;i++){
           if (pre[0][i]==2)
           pre[1][i]++;//每个位置个数是否是2
        }
        for (int i=1;i<=n;i++){
            pre[1][i]+=pre[1][i-1];//次数==2的前i项个数
        }
        for (int i=1;i<=q;i++){
           for (int j=a[i].l;j<=a[i].r;j++){
             if (pre[0][j]==1){
                pre[2][i]++;//在第i个区间内部有多少个数为1的个数
             }
           }
        }
        for (int i=1;i<=q;i++){
           for (int j=i+1;j<=q;j++){
               int l=max(a[i].l,a[j].l);
               int r=min(a[i].r,a[j].r);
               int tmp=sum;
               if (r>=l)
               tmp-=(pre[1][r]-pre[1][l-1]);
               tmp-=(pre[2][j]+pre[2][i]);
               ans=max(ans,tmp);
           }
        }
       printf("%d
    ",ans);
      }
      return 0;
    }

    那么我们考虑DP做法。

    这个DP和我前几天做的DP非常像。

    我们可以构建这样DP

    DP[i]代表,选到i位置,选择k个的最优解。用L[i]表示某个覆盖到i位置的区间,往左能覆盖的最小坐标

    那么转移方程就是:

    dp[j]=max(dp[L[j]-1]+j-L[j]+1,dp[j])

    同时要是短的段落,能覆盖的个数是大于当前的,可以更新更长的段。

    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #define rep(i,j,k) for(int i=j;i<=k;i++)
    #define per(i,j,k) for(int i=j;i>=k;i--)
    using namespace std;
    const int maxx = 5000+50;
    int dp[maxx];
    int L[maxx];
    int main()
    {
        int n,q,tmp,l,r;
        while(~scanf("%d%d",&n,&q))
        {
            rep(i,1,n)
            {
                L[i]=i+1;
                dp[i]=0;
            }
            rep(i,1,q)
            {
                scanf("%d%d",&l,&r);
                rep(j,l,r)
                {
                    L[j]=min(L[j],l);
                }
            }
    //        for (int i=1;i<=n;i++){
    //            printf("%d ",L[i]);
    //        }
    //        cout<<endl;
            rep(i,1,q-2)
            {
                per(j,n,1)
                {
                    dp[j]=max(dp[L[j]-1]+j-L[j]+1,dp[j]);
                }
                rep(j,1,n){
                    dp[j]=max(dp[j],dp[j-1]);
                }
            }
            printf("%d
    ",dp[n]);
        }
        return 0;
    }
    有不懂欢迎咨询 QQ:1326487164(添加时记得备注)
  • 相关阅读:
    Ubuntu分区挂载
    YOLOv3:Demo needs OpenCV for webcam images
    tf.strided_slice函数
    numpy:np.random.seed()
    python:split()函数
    python:set() 函数
    python:zip() 函数
    python:enumerate 函数
    电脑无法上网,DNS出现fec0:0:0:ffff::1%1问题
    python:map 函数
  • 原文地址:https://www.cnblogs.com/bluefly-hrbust/p/10623928.html
Copyright © 2011-2022 走看看