标签:name long sizeof 感染 std 因此 algo size ons
题目链接: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 }
标签:name long sizeof 感染 std 因此 algo size ons
原文地址:https://www.cnblogs.com/hzoi-poozhai/p/12657646.html