JSOI2016病毒感染

题目链接:https://www.luogu.com.cn/problem/P5774

1.题目大意:

  有1-n的村庄,每个村庄在不治疗的情况下每天死 a[ i ] 人,到达一个村庄可以治疗或跳过, 若跳过, 再回头时只能一直走回这个村庄, 然后才能重新往前走,求最少死亡人数。

 

2.题目分析

  我们定义f[ i ]为前 i 个村庄全部治好的最小代价。令j < i, 在 1-i 内枚举回头点, 因此我们再定义一个 g[ i ][ j ] 数组表示从 i 走到 j 再走回 i 的最小代价, 作为辅助数组, 在dp的时候辅助求出 f[ i ] 的值。

  预处理一下前缀和 s[] 数组

3.动态转移方程推导

       (1) g数组:

     我们枚举起点 i 和 区间长度 j , 则终点为 i + j

     <a>  若救 i

       w[i+1][i+j] + (s[i+j]-s[i])*2;

       救 i 花了一天, 从 i 走到 i+1 花了一天,总共耽误两天, 死亡人数 为 i+1 到 j 的总人数 乘以 2, 即  (s[i+j]-s[i])*2

     <b>若跳过 i

       w[i+1][i+j] + 3*j*a[i]+s[i+j]-s[i]

       从 i 走到 j 再走回 i 并治疗从 i+1 到 j 的所有村庄, 耗时 3*j 天。 i 村庄死亡 3*j*a[ i ];

       从 i 到 i+1 花了一天, 死亡 s[i+j]-s[i]

  综上  w[i][i+j] = w[i+1][i+j]+min((s[i+j]-s[i])*2, 3*j*a[i]+s[i+j]-s[i]);

 

(2)f数组

    枚举终点 i 和 回头点 j

f[i] = min(f[i], f[j]+w[j+1][i]+(3*(i-j-1)+i-j+1)*(s[N]-s[i]));

              从 j+1 走到 i 回到 j+1, 我们还需再到 i ,耗时 3*(i-(j+1))

    从·j 走到 j+1 并治疗 j+1 到 i 的所有村庄 耗时 i-j+1(不理解可以自己画线段图看一下)

    共死亡(3*(i-j-1)+i-j+1)*(s[N]-s[i]))

4.最后附上代码

 

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<iostream>
 5 typedef long long ll;
 6 using namespace std;
 7 const int maxn = 3010;
 8 ll a[maxn], s[maxn], f[maxn], w[maxn][maxn];
 9 int main(){
10     ll N; scanf("%lld", &N);
11     for(int i=1; i<=N; i++){
12         scanf("%lld", &a[i]);
13         s[i] = s[i-1] + a[i];
14     }
15     for(int j=1; j<N; j++){
16         for(int i=1; i<=N-j; i++){
17             w[i][i+j] = w[i+1][i+j]+min((s[i+j]-s[i])*2, 3*j*a[i]+s[i+j]-s[i]);
18         }
19     }
20     memset(f, 0x3f, sizeof(f));
21     f[0] = 0;
22     for(int i=1; i<=N; i++){
23         for(int j=0; j<i; j++){
24             f[i] = min(f[i], f[j]+w[j+1][i]+(3*(i-j-1)+i-j+1)*(s[N]-s[i]));
25         }
26     }
27     printf("%lld\n",f[N]);
28     return 0;
29 }
posted @ 2020-04-08 07:33  poozhai  阅读(202)  评论(0编辑  收藏  举报