zoukankan      html  css  js  c++  java
  • codeforces 1197D-Yet Another Subarray Problem

    传送门:QAQQAQ

    题意:给你一个序列,求一个子序列a[l]~a[r]使得该子序列的sum(l,r)-k*(r-l+1+m+1)/m值是在所有子序列中最大的,并输出最大值

    思路:比赛的时候使用O(n)写的,但是被hack了,因为O(n)无法记录当前距离下一次-k还有多少,若用单调队列维护也不知道前面应该弹出多少(可能现在把前面弹出是最优的,但是到后面可能因为个数还没到m的倍数,把前面加进去又是最优的),所以我们考虑再加一维

    法一:dp[i][j]表示序列到i截止,这一轮已经进行了j次取数(j=(len+m-1)%m),那么dp[i][j]维护的就是起点为s=i-j+1-m*t(t>=0)这个集合的最优,这样所有的dp[i][j]就可以维护以i截止的最优答案了

    对于当前i更新有两种情况:第一种是只取当前这个a[i]这个在dp[i][1]特判即可

                第二种是还要取前面的,dp[i][j]从dp[i-1][j-1](因为dp[i-1][j-1]所维护的s集合和dp[i][j]所维护的s集合是一样的)转移即可(注意边界条件)

    (之前HCY玄学又加了一层循环更新,一直看不懂,后来发现他虽然答案是对的,但m次更新中只有1次是有效的)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const ll inf=200000000000000;
     
    int n,m;
    ll ans=0,dp[300005][20],sum[300005],a[300005],k;
     
    int main()
    {
        scanf("%d%d%lld",&n,&m,&k);
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            sum[i]=sum[i-1]+a[i];
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=m;j++) dp[i][j]=-inf;
        }
        dp[1][1]=a[1]-k; 
        for(int i=2;i<=n;i++)
        {
            dp[i][1]=a[i]-k;
            for(int j=1;j<=min(i,m);j++)
            {
                if(j==1) dp[i][j]=max(dp[i][j],dp[i-1][m]+a[i]-k);
                else dp[i][j]=max(dp[i][j],dp[i-1][j-1]+a[i]); 
            }
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++) ans=max(ans,dp[i][j]);
        }
        cout<<ans<<endl;
        return 0;
    }//

    法二:

    和比赛时我的思路很像,只不过这里多加了一层维护start_point%m=rnd,进行m次尺取法即可(在时间够的情况下,搞不清楚当前单调队列弹出几个是最优的,那么就枚举,这样就不用担心前面要弹出什么了,只需在len%m=0时判断是否要把起始点重置即可)

    即如果当前的和减去k*t小于0,那么就重新开始,否则继续加

    注意ans在每一次向后扩展时都要更新

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=300005;
    ll ans=0,n,a[N],m,k;
     
    int main()
    {
        scanf("%lld%lld%lld",&n,&m,&k);
        for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
        for(int rnd=1;rnd<=m;rnd++)
        {
            ll len=0; ll now=0;
            for(int i=rnd;i<=n;i++)
            {
                if(len%m==0) if(now-len/m*k<0) now=0,len=0;
                now+=a[i]; len++;
                ans=max(ans,now-(len+m-1)/m*k);
            }
        }
        cout<<ans<<endl;
        return 0;
    }
  • 相关阅读:
    C#基础第五天-作业-用DataTable制作名片集
    C#基础第四天-作业答案-Hashtable-list<KeyValuePair>泛型实现名片
    C#基础第四天-作业-Hashtable-list<KeyValuePair>泛型实现名片
    C#基础第三天-作业答案-集合-冒泡排序-模拟名片
    C#基础第三天-作业-集合-冒泡排序-模拟名片
    C#基础第二天-作业答案-九九乘法表-打印星星
    C#基础第二天-作业-九九乘法表-打印星星
    C#基础第一天-作业答案
    C#基础第一天-作业
    C#-string.Format对C#字符串格式化
  • 原文地址:https://www.cnblogs.com/Forever-666/p/11241525.html
Copyright © 2011-2022 走看看