#dp#洛谷 5774 [JSOI2016]病毒感染
阿新 • • 發佈:2020-11-04
分析
此題肯定不是綠題,哪有這麼噁心的dp
試想這樣的情形:假設當 JYY 第一次抵達村莊 \(i\),未作救治並直接前往了另一個村莊。那麼由於 \(i\) 村莊的人們求生心切,
一旦當 JYY 朝向靠近 \(i\) 村莊的方向前行時,\(i\) 村莊的村民就會以為 JYY 是來救他們了,而產生巨大的期望。
之後倘若 JYY 再次掉頭朝著遠離 \(i\) 村莊的方向行進,那麼 \(i\) 村莊的村民就會因為巨大的失落而產生絕望的情緒。
所以JYY應該是一段一段治癒的,設\(dp[i]\)表示JYY治癒完前\(i\)個村莊的最少不幸人數
\(dp[i]=\min\{dp[j]+???(calc(j+1,i))+(s[n]-s[i])*(???)\}\)
首先這一段應該是從\(j+1\)到\(i\)再到\(j+1\)再到\(i\),先考慮後面的天數就是\(4*(i-j-1)+1+1\),
也就是往返三遍再治癒當中所有村民總計4遍(治癒要加1),還要加上從\(j\)到\(j+1\)的天數
考慮中間\(calc\)的部分,\(calc(j,i)\)可以選擇治癒\(j\)先(\(j+1\sim i\)都拖延1天)或者先治癒\(j+1\sim i\)再回來治癒\(j\),
那也就是 \[calc(j,i)=calc(j+1,i)+s[i]-s[j]+\min\{3*(i-j)*a[j],s[i]-s[j]\} \]
正序列舉\(i\)倒序列舉\(j\)就可以做到\(O(n^2)\)
綜上所述
程式碼
#include <cstdio> #include <cctype> #include <cstring> #define rr register using namespace std; const int N=3011; typedef long long lll; lll a[N],s[N],dp[N],f[N][N],n; inline signed iut(){ rr int ans=0; rr char c=getchar(); while (!isdigit(c)) c=getchar(); while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar(); return ans; } inline lll calc(int l,int r){return s[r]-s[l-1];} inline lll min(lll a,lll b){return a<b?a:b;} signed main(){ n=iut(),memset(dp,42,sizeof(dp)),dp[0]=0; for (rr int i=1;i<=n;++i) s[i]=s[i-1]+(a[i]=iut()); for (rr int i=1;i<=n;++i){ f[i][i]=0; for (rr int j=i-1;j;--j) f[j][i]=f[j+1][i]+calc(j+1,i)+min(3*(i-j)*a[j],calc(j+1,i)); } for (rr int i=1;i<=n;++i) for (rr int j=0;j<i;++j) dp[i]=min(dp[i],dp[j]+f[j+1][i]+((i-j-1)<<2|2)*calc(i+1,n)); return !printf("%lld",dp[n]); }