P5774 [JSOI2016]病毒感染
阿新 • • 發佈:2020-11-01
大致題意
有\(n\)個疫情城市,每個城市\(i\)在未被治癒時每天會死\(a_i\)個人,現在從一號城市出發,每一天可以選擇:
花費一天時間徹底治癒目前所在的城市的所有患者。這一天不會有任何患者死去;
花費一天的時間前往一個相鄰的村莊,若往左走則需要沿路把左邊所有未治癒的城市治癒。
求最少死亡人數。
分析
設\(f_i\)表示前\(i\)個城市的最少死亡人數
\(w(i,j)\)表示從\(i\)走到\(j\)再走回\(i\)時的最少死亡人數
\(sum_i\)表示城市每日死亡人數的字首和
可以看出是個經(tao)典(lu)\(dp\),有顯然的轉移方程
\(f_i = min(f_j+w(j+1,i)+(4×(i-j)-2)×(sum_n-sum_i))\)
問題在於如何來搞出\(w(i,j)\)
\(w(i,j)\)具有最有子結構性質,考慮使用區間\(dp\)的方法來預處理
顯然,一個城市至多隻會被經過兩次,因此一個城市被治癒的時間只有兩種可能:
1.在第一次被經過時被治癒
2.在第二次被經過時治癒
若第一次經過時花費一天的時間去治癒,後面的城市都會多死亡\(sum_j - sum_i\)個人,反之該城市需要經過\(3×j\)天后才能被治癒,也就是會多死亡\(3×(j-i)×a_i\)個人
得到轉移方程:
\(w(i,j) = w(i+1,j)+min(2×(sum_j-sum_i),a_i×3×(j-i)+sum_j-sum_i)\)
\(code\)
#include<bits/stdc++.h> using namespace std; const int MAXN = 3010; long long n,a[MAXN]; long long f[MAXN][MAXN],sum[MAXN]; long long ans[MAXN]; int main(){ cin>>n; for(int i=1;i<=n;i++){ scanf("%lld",&a[i]); sum[i] = sum[i-1]+a[i]; } memset(ans,0x3f,sizeof(ans)); memset(f,0x3f,sizeof(f)); for(int i=1;i<=n;i++){ f[i][i] = 0; } for(int len=1;len<=n-1;len++){ for(int i=1;i+len<=n;i++){ int j=i+len; f[i][j] = f[i+1][j]+min(2*(sum[j]-sum[i]),a[i]*3*len+sum[j]-sum[i]); } } ans[0]=0; for(int i=1;i<=n;i++){ for(int j=0;j<i;j++){ ans[i] = min(ans[j]+f[j+1][i]+(4*(i-j)-2)*(sum[n]-sum[i]),ans[i]); } } cout<<ans[n]<<endl; }