P5774 病毒感染
题面
有\(n\)个小镇,每个小镇\(a_i\)个人感染了病毒,\(JYY\)每到一个村庄可以选择救治该村所有的人(这一天没有人死亡),或者去下一个村庄,当他已经跳过一个村庄后再向该村庄走时(此处不明白请仔细看题目中绝对值的关系那个地方),必须来救治该村庄
第\(i\)个村假设有\(a_i\)个人感染,那么第二天这\(a_i\)个人会死亡且会新感染\(a_i\)个人,求最小死亡数
\(1\le n\le3000,1\le a_i\le10^9\)
思路
这题真的是普及+/提高吗?
- \(50pts\)
定义\(f[i]\)为治愈前\(i\)个村庄的最小死亡数
对于每个村庄来说\(JYY\)可以选择治疗还是不治疗,重点在于怎么处理回去这的问题
由题目中\(|k-i|<|k-j|\)可得只要想从\(j\)回去治疗\(k\),那就必须把之前的全都治了
所以我们只需要枚举\(k\)即可实现转移
复杂度\(O(n^3)\),期望得分:\(50\)
- \(100pts\)
上面的做法时间主要浪费在计算略过村庄再回来的死亡人数上,我们可以考虑预处理这一部分
定义\(g[i][j]\)表示从\(i\)到\(j\)在回到\(i\)的最少死亡人数
但是我不知道\(g[i][j]\)应该怎么转移(看不懂题解),先挖个坑,以后回来写完
回来填坑了
枚举\(i\)为起点,\(j\)为长度,\(sum[i]=\sum\limits_{k=1}^{i}a[k]\)\(g[j][i+j]=g[j+1][i+j]+\min((sum[i+j]-sum[j])*2,a[j]*i*3+sum[i+j]-sum[j])\)
则设\(f[i]\)为前\(i\)个村庄消灭疫情最小死亡人数
\(f[i]=\min(f[j]+g[j+1][i]+(4*i-4*j-2)*(sum[n]-sum[i]))\)
淦,这转移真恶心,如果看不懂请点这里
code
/*
@ author:pyyyyyy/guhl37
-----思路------
-----debug-------
忘记初始化了,qwq
*/
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3020;
int n,a[N],f[N],sum[N],g[N][N];
signed main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
cin>>n;
for(int i=1;i<=n;++i) cin>>a[i];
for(int i=1;i<=n;++i) sum[i]=sum[i-1]+a[i];
for(int i=1;i<=n-1;++i)
for(int j=1;j<=n;++j)
if(i+j<=n)
g[j][i+j]=g[j+1][i+j]+min((sum[i+j]-sum[j])*2,a[j]*i*3+sum[i+j]-sum[j]);
memset(f,0x3f3f3f,sizeof(f));f[0]=0;
for(int i=1;i<=n;++i)
for(int j=0;j<i;++j)
f[i]=min(f[j]+g[j+1][i]+(4*i-4*j-2)*(sum[n]-sum[i]),f[i]);
cout<<f[n];
return 0;
}