• CodeForces 1396C


    首先扯一句题外话:B 出题人 ****。nmd 老子开学前一天打你午夜的比赛,还出个结论题糟蹋我比赛体验?还好把这个 C 做出来了,要不然玩完了。

    洛谷题目页面传送门 & CF 题目页面传送门

    题意见洛谷。

    注意到一个 boss 的血量为 (2),于是每一级最多会被强制离开 (1) 次。

    于是考虑对于每一级决定是否被强制离开。设若不离开,那么第 (i) 级最少需要 (x_i) 的时间打怪;若离开,那么第 (i) 级最少需要 (y_i) 的时间打怪(这个 (x_i,y_i) 太好算了,显然可以 (mathrm O(1)),就不再赘述了)。显然,对于一种决定,总时间就是它们的打怪时间相加(虽然被强制离开的级别的打怪过程不是连续的,但也可以相加)加上走路的最小总时间。不妨来研究对于任意一个决定,走路的最优路线长什么样子。

    若没有被强制离开的级别的话,最优路线显然是从 (1) 笔直走到 (n)。如果有的话,那么那些被强制离开的点要经过 (2) 次(这是显然的吧)。按原笔直路线经过它们的时候,它们只被经过了 (1) 次;要想来个第 (2) 次,肯定要回头;然后还要继续往前走啊,于是回头走到某处的时候还要再回头按原笔直路线走。于是很容易得出结论:最优路线一定是在笔直路线的基础上,有若干个不相交的区间多走了 (2) 遍,其中这些区间要覆盖所有的被强制离开的点。

    细节:

    1. 每个区间的大小要 (>1),因为 (=1) 的话就是原地打转,是不算经过多遍的;
    2. (n) 处不被强制离开的话,那么右端点为 (n) 的区间是只需要再多走 (1) 遍的,因为到达了左端点之后就没必要再回头了,游戏已经 win 了。

    接下来就很容易设计出无后效性的 DP 了(在得出结论之前,瓶颈在于可以往两边跑,后效性消除不了)。(dp_i) 表示考虑到第 (i) 级,停在第 (i) 级且前 (i) 级都打掉了时的最小打怪、额外走路总时间。边界:(dp_0=0,dp_1=x_1);目标:(dp_n+(n-1)d)。转移的话,考虑第 (i) 级是离开还是不离开两种选择:不离开简单要死;离开的话,显然 (i) 是最后一个重复走区间的右端点,于是枚举左端点。所以状态转移方程:

    [dp_i=min!left(dp_{i-1}+x_i,minlimits_{j=0}^{i-2}!left{dp_j+sumlimits_{k=j+1}^imin(x_k,y_k)+2(i-j-1)d ight} ight) ]

    直接算是 (mathrm O!left(n^3 ight)) 的。里层 (sum) 可以前缀和优化掉,剩下来简单拆一下 (min),发现就是个前缀 (min),稍微 two-pointers 简单维护一下就优化成了 (mathrm O(n))。注意,(i=n) 要特殊算,随便跑一下即可,不影响复杂度。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    const int inf=0x3f3f3f3f3f3f3f3f;
    const int N=1000000;
    int n;
    int r1,r2,r3,d;
    int a[N+1];
    int dp[N+1];
    int x[N+1],y[N+1];
    int Sum[N+1];
    signed main(){
    	cin>>n>>r1>>r2>>r3>>d;
    	for(int i=1;i<=n;i++)scanf("%lld",a+i);
    	for(int i=1;i<=n;i++){
    		x[i]=a[i]*min(r1,r3)+r3;
    		y[i]=min(r2+min(min(r1,r2),r3),a[i]*min(r1,r3)+min(r1,r2)+min(min(r1,r2),r3));
    		Sum[i]=Sum[i-1]+min(x[i],y[i]);
    //		cout<<x[i]<<" "<<y[i]<<"
    ";
    	}
    	int mn=inf;
    	dp[1]=x[1];
    	for(int i=2;i<n;i++){
    		mn=min(mn,dp[i-2]-Sum[i-2]-2*(i-1)*d);
    		dp[i]=min(dp[i-1]+x[i],mn+Sum[i]+2*i*d);
    //		cout<<dp[i]<<"
    ";
    	}
    	dp[n]=dp[n-1]+x[n];
    	for(int i=0;i<=n-2;i++)
    		dp[n]=min(dp[n],dp[i]+Sum[n]-Sum[i]+2*n*d-2*(i+1)*d),
    		dp[n]=min(dp[n],dp[i]+Sum[n-1]-Sum[i]+x[n]+n*d-(i+1)*d);
    	cout<<dp[n]+(n-1)*d;
    	return 0;
    }
    
    珍爱生命,远离抄袭!
  • 相关阅读:
    根据索引删除数组内信息时导致程序崩溃
    C/C++判断字符串是否包含某个子字符串
    Qwidget布局操作之QGridLayout(网格布局)
    Qt获取文件路径、文件夹路径
    javascript DOM document属性
    javascript dom页面中的location属性
    javascript页面常用事件
    python的高阶函数式编程
    python set 集合复习--点滴
    python异常
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/CodeForces-1396C.html
Copyright © 2020-2023  润新知