zoukankan      html  css  js  c++  java
  • CF1016C Vasya And The Mushrooms 题解

    这道题一眼就是一个DP(大雾),但题目中有一句很有趣的话

    “He wants to visit all the cells exactly once and maximize the total weight of the collected mushrooms.”

    也就是每个格子必须且只能经过一次,而且只有两派格子,所以是不是一目了然的只有两种行走路线。

    其实不然,你还可以这么走

    或者,这么走

    也就是把前两种行走方式结合一下

                          

    不过也没有变得不可做,因为一定是先用第一种行走方式,再接第二种;不可能存在先走第二种,再第一种的情况(都已经拐回去了,还走啥啊)。所以枚举一下从何时开始更换行走方式即可。这两种行走方式吃到的蘑菇都可以先预处理出来。

    a1是第一行的蘑菇生长率,a2是第二行的

    因为观察到无论从何时改变行走方式,都可以看成是在走完一个整列后再改变,所以所有的数组都是以列数作为下标的。

    首先预处理蛇形行走方式:

    两行两行处理,每次走一个U型,要注意蘑菇不是从第1秒开始长的(其实是时间是从 0 s 开始记的,不过一样哈)

    init_b(){
        //b[i]表示蛇形在取完第i列的格子的蘑菇后的 
        //第一个格子为第1s(但不吃蘑菇)
        //这个格子蘑菇数为(i-1)*a 
        int s=0;
        for(int i=1;i<=n;i+=2){
            s+=a1[i]*(i*2-2);
            s+=a2[i]*(i*2-1);
            b[i]=s;
            s+=a2[i+1]*(i*2);
            s+=a1[i+1]*(i*2+1);
            b[i+1]=s;
            //走了一个U型 ↓→↑
        }//走了一个S型 
    }

    再预处理环形行走方式:

    这样的一条路可以看成是的一条路加上蓝色的两个格子的蘑菇。

    不过原来路上的每只蘑菇都多生长了 1 s,所以要加一个“后缀和”。

    init_S(){
        //S[i]表示从第i列(从左往右数)以及右边的半圈的蘑菇生长率之和 
        int s=0;
        for(int i=n;i>=1;i--)
        s+=a1[i]+a2[i],S[n-i+1]=s;
    }

    如果是从上绕到下,上面的格子生长时间是0s(还没有开始长,也不用加),下面的格子生长时间是后面的两列格子数+上面的一个格子=(l*2+1),l 是后面的长度;

    如果是从下绕到上,相反就好了(没人会告诉你还有一个东西叫复制粘贴)

    init_C1(){
        //从上面绕下去
        //c1[i]表示从第i列(从左往右数)开始的 
        int s=0;
        for(int i=n,l=0;i>=1;i--,l++){
            s+=S[l];
            s+=a2[n-l]*(l+l+1);
            c1[i]=s;
        } 
    //    for(int i=1;i<=n;i++)
    //    cout<<c1[i]<<" ";
    }
    init_C2(){
        //从下面绕上去
        //c2[i]表示从第i列(从左往右数)开始的 
        int s=0;
        for(int i=n,l=0;i>=1;i--,l++){
            s+=S[l];
            s+=a1[n-l]*(l+l+1);//只有这里的a1,a2换了一下 
            c2[i]=s;
        } 
    //    for(int i=1;i<=n;i++)
    //    cout<<c2[i]<<" ";
    }

    最后是统计答案(程序主体):

    经过观察,我们发现,从上往下绕,只能发生在走完偶数列格子后(一开始就开始绕可以看成是从第0列就开始拐弯),因为只有这时最后一步才踩在上面一行格子上。

    而从下往上绕,只能在走过奇数列格子后,这时落脚在下面一行,可以开始从下往上绕。

    因为在绕之前已经经过了i列(2*i个格子),所以后面绕的所有格子都多生长了2*i秒,再加上这些后缀和*生长时间 就是这么走一趟的收获了。与答案求max。

    //枚举从哪开始拐,上拐下拐 
        for(int i=0;i<=n;i++){
            //从下一列开始拐弯 
            if(i%2) ans=max(ans,b[i]+c2[i+1]+S[n-i]*(i*2));
            else    ans=max(ans,b[i]+c1[i+1]+S[n-i]*(i*2));
        }

    就结束了。

    下面贴上完整简洁(压行)版代码

    #include<iostream>
    #include<cstdio>
    #define N 300100
    #define int long long
    using namespace std;
    int n,ans,a1[N],a2[N],S[N],b[N],c1[N],c2[N];
    init_S(){
        int s=0;
        for(int i=n;i>=1;i--)
        s+=a1[i]+a2[i],S[n-i+1]=s;
    }
    init_b(){
        int s=0;
        for(int i=1;i<=n;i+=2){
            s+=a1[i]*(i*2-2);
            s+=a2[i]*(i*2-1);
            b[i]=s;
            s+=a2[i+1]*(i*2);
            s+=a1[i+1]*(i*2+1);
            b[i+1]=s;
        }
    }
    init_C1(){
        int s=0;
        for(int i=n,l=0;i>=1;i--,l++){
            s+=S[l];s+=a2[n-l]*(l+l+1);
            c1[i]=s;
        }
    }
    init_C2(){
        int s=0;
        for(int i=n,l=0;i>=1;i--,l++){
            s+=S[l];s+=a1[n-l]*(l+l+1);
            c2[i]=s;
        }
    }
    signed main(){
        cin>>n;
        for(int i=1;i<=n;i++)cin>>a1[i];
        for(int i=1;i<=n;i++)cin>>a2[i];
        init_S(); 
        init_b(); 
        init_C1();
        init_C2();
        for(int i=0;i<=n;i++){
            if(i%2) ans=max(ans,b[i]+c2[i+1]+S[n-i]*(i*2));
            else    ans=max(ans,b[i]+c1[i+1]+S[n-i]*(i*2));
        }
        cout<<ans;
    }
  • 相关阅读:
    OpenCV在MFC图像控件内显示图像
    Android APK反编译具体解释(附图)
    Android下用Properties保存程序配置
    王灏:光音网络致力打造Wi-Fi大生态圈
    解决ccSvcHst.exe CPU占用超50%的问题,及其缘由
    配置管理工具比較
    应用程序无法正常启动0xc0150002 解决方式
    现有一些开源ESB总线的比較
    使用GridView自带分页的代码
    Hadoop 2.4.0新特性介绍
  • 原文地址:https://www.cnblogs.com/jzzcjb/p/9496661.html
Copyright © 2011-2022 走看看