• P5774 [JSOI2016]病毒感染


    题目描述

    JOSI 的边陲小镇爆发了严重的 Jebola 病毒疫情,大批群众感染生命垂危。计算机科学家 JYY 采用最新的算法紧急研制出了 Jebola 疫苗,并火速前往灾区救治患者。

    一共有 NN 个小镇爆发了 Jebola 疫情。这些小镇由于地处边陲,仅仅通过一条长直公路连接。方便起见我们将这些小镇按照公路连接顺序由 11 编号到 NN。JYY 会在第一天一早抵达 11 号小镇。

    一开始在 ii 号小镇,有 a_iai 名患者感染了 Jebola 病毒。

    每一天 JYY 可以选择:

    1. 花费一天时间彻底治愈 JYY 目前所在的村庄的所有 Jebola 患者。这一天不会有任何患者死去;
    2. 花费一天的时间前往一个相邻的村庄。

    当一天开始时,如果一个村庄里有 kk 个 Jebola 患者,那么这一天结束时,这 kk 个患者会感染另外 kk 个这个村子里的健康村民并死去。所以对于 ii 号村庄,只要这个村庄没有被 JYY 彻底消灭疫情,那么每一天都会有 a_iai 个村民死去。

    JYY 希望采用措施使得疫情被整体消灭时,总共死去的村民数量尽量少。

    为了达成这一目标,JYY 有时会选择抵达一个村庄但是并不对村民进行施救。这样的行为如果不加限制,往往会造成更加严重的后果。

    试想这样的情形:假设当 JYY 第一次抵达村庄 ii,未作救治并直接前往了另一个村庄。那么由于 ii 村庄的人们求生心切,一旦当 JYY 朝向靠近 ii 村庄的方向前行时,ii 村庄的村民就会以为 JYY 是来救他们了,而产生巨大的期望。之后倘若 JYY 再次掉头朝着远离 ii 村庄的方向行进,那么 ii 村庄的村民就会因为巨大的失落而产生绝望的情绪。

    为了避免这种情况,JYY 对他的行程做了如下规定:

    假设 JYY 进入 ii 村庄并在第二天立即离开(村庄 ii 的疫情并未治愈)。如果在之后的某一天,JYY 从村庄 jj 前往村庄 kk,并满足 |k-i| lt |k-j|ki<kj∣。那么在之后的日子里 JYY 只能朝着 ii 村庄前进直到抵达 ii 村庄并立即治愈该村的患者。在前往 ii 村庄的过程中,JYY 可以选择将途经村庄的疫情治愈。

    比如,如果 JYY 有如下行程:

    第一天:从村庄 11 前往村庄 22;

    第二天:从村庄 22 前往村庄 33;

    第三天:治愈村庄 33;

    第四天:前往村庄 22。

    此时 JYY 对于之后三天的行程只有唯一一种选择:

    第五天:治愈村庄 22;

    第六天:前往村庄 11;

    第七天:治愈村庄 11。

    JYY 想知道在治愈所有村庄之前,至少会有多少村民因 Jebola 死去。

    输入格式

    输入第一行包含一个正整数 NN;

    接下来一行包含 NN 个整数,分别为 a_1,a_2,...,a_Na1,a2,...,aN

    输出格式

    输出一行一个整数,表示最优行程安排下会死去的村民数量。

    输入输出样例

    输入 #1
    6
    40 200 1 300 2 10
    输出 #1
    1950
    看一下代码:(代码还未交,如有问题欢迎回复)
    #include<cstdio>
    #include<iostream>
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int n=3000+10;
    ll a[n];
    ll s[n];
    ll f[n];//治愈前i个村庄并停到i的最少死亡的人数
            //f数组转移时枚举断点,用w数组辅助转移
    ll w[n][n];//从i出发治愈[i,j]中所有村庄再回到i的最少死亡人数
               //在这里我们假设1-(i-1)已经治愈好了,i-n没有被救
               //我们从w[i+1][j]转移w[i][j](区间dp),在计算时w[i-1][j]还没被计算
    ll N;
    
    int main(){
        scanf("%lld",&N);
        for(int i=1;i<=N;i++){
            scanf("%lld",&a[i]);
            s[i]=s[i-1]+a[i];
        }
        for(int i=1;i<=N;i++){
            w[i][i]=s[N]-s[i];
        }
        //len是枚举的长度变量,这是一个dp方法,表示这次dp的区间长度,也可不用
        for(int j=1;j<=N;j++){
            for(int i=j-1;i;--i){
                w[i][j]=w[i+1][j]+min(2*(s[N]-s[i])+s[N]-s[j],3*a[i]*(j-i)+2*(s[N]-s[j])+s[N]-s[i]);
                //第一种策略:经过i时直接治疗,我们花费两天(治疗i一天,到达i+1一天),所以2*(s[n]-s[i]),我们要从i+1回到i,所以s[n]-s[j]
                /*第二种策略:先越过去,回来的时候再治,所以有从i+1到i一天的代价,然后救i一天,所以2*(s[n]-s[j]),s[n]-s[i]已经计算了i到i+1
                这一天代价,我们计算在这期间耽误多少天,就是j-i+j-i+j-i(过去,回来,救治),s[n]-s[i],这里是从i到j时,经过它的,为什么不乘2,
                再回来的时候i+1已经治好了,肯定治好了,第二次经过一个电时,这个点一定已经治好了*/
            }
        }
        memset(f,0x3f,sizeof(f));
        f[0]=0;//边界dp(f)为0;
        for(int i=1;i<=N;i++){
            for(int j=0;j<i;j++){
                f[i]=min(f[i],f[j]+(j!=0)*(s[N]-s[j])+w[j+1][i]+(i-j-1)*s[N]-s[j]);//这里i,j不要看混了
                //枚举断点,这里表示从i返回j+1,再回到i这是一次往返所以用w[j+1][i],表示j+1到i的时候并不是一次全治好
                //因为最后数组停在了j+1,需要从j+1返回到i,所以i-(j+1)
                //(j!=0)意思是j如果不是0,就为1,否则为0
            }
        }
        printf("%lld
    ",f[N]);
        return 0;
    }

    说明/提示

    样例说明

    我们用 C(k)C(k) 表示治愈 kk 号村庄,i ightarrow jij 表示从村庄 ii 前进到村庄 jj,用逗号分隔每一天的行程安排,那么样例中的最优策略为:

    1 ightarrow 2 , C(2),2 ightarrow 3 , 3 ightarrow 4 , C(4) , 4 ightarrow 3 , C(3) , 3 ightarrow 2 , 2 ightarrow 1 , C(1) , 1 ightarrow 2 , 2 ightarrow 3 , 3 ightarrow 4 , 4 ightarrow 5 , 5 ightarrow 6 , C(6) , 6 ightarrow 5 , C(5)12,C(2),23,34,C(4),43,C(3),32,21,C(1),12,23,34,45,56,C(6),65,C(5);

    整个过程耗时 1818 天。


    数据范围

    对于 10\%10% 的数据,满足 N le 10N10;

    对于 30\%30% 的数据,满足 N le 20N20;

    对于 50\%50% 的数据,满足 N le 60N60;

    对于 100\%100% 的数据,满足 1 le N le 30001N3000,1 le a_i le 10^91ai109。

  • 相关阅读:
    用一个变量表示 ----------"序号,名称,价格"
    11.3 字典复习
    python闭包使用
    Selenium with Python使用心得
    laravel队列使用
    display:inline-block笔记
    python mysql设置当前连接默认的字符集
    lavavel门面(facade)分析
    系统软件漏洞修复最佳实践
    记一次浮点数比较
  • 原文地址:https://www.cnblogs.com/LightyaChoo/p/12643192.html
Copyright © 2020-2023  润新知