1. 程式人生 > 實用技巧 >P5774 [JSOI2016]病毒感染

P5774 [JSOI2016]病毒感染

間隙

大致題意

\(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;
}