JSOI 2016 病毒感染 輔助Dp問題

2022-02-03 07:02:06 字數 2719 閱讀 5197

直接看這道題,第乙個困惑點,那個絕對值的比較是什麼東西,根據數學知識,我們可以知道這個意思是k到i的距離小於k到j的距離,而路線是線性的,這就意味著當且僅當k在j的左邊時才成立,不然總會有k-i>k-j,還不理解?看下圖

如果k在k'的位置,那麼k-i一定大於k-j吧,所以這個題的題意是只要從j往回走去**k,就必須把之前沒**過的村莊也**了。

想到這裡,狀態就差不多出來了,定義dp[i]表示**前i個村莊的最小死亡數,下面考慮狀態轉移,對於jyy來說,每個村莊它都有兩個選擇,**or先去別的再走回來**,**的話很好弄,主要考慮的就是略過它的情況,這時候如果依次列舉k,效率應該是n^3,程式吃不消,3000的極限資料我們最少也要壓到n^2左右,所以接下來考慮優化。

優化其實也挺簡單的,主要有一點很噁心,下邊再說。(從這裡開始預設j在i的前邊,請勿被上圖迷惑)我們發現多出來的時間主要是用在了計算略過村莊再回來的死亡人數的計算,所以我們可以先考慮預處理出從j到i再從i到j然後又回到i這一過程中最少的死亡數,於是定義g[i][j]含義為上述的來輔助我們的dp。我還是補一張圖吧……把我自己繞懵了

看了這張圖我相信你就明白了g陣列的含義,接下來考慮如何求解g陣列,初始的話g[i][i]肯定是為0的,所以轉移都應該從這個位置開始,即倒序,那麼怎麼轉移呢,接下來就是很噁心的乙個地方,計算經過的天數!很多題解裡都沒寫到這個,這裡詳細計算一下。

對於g[i][j],同樣分兩種情況討論,救助或是略過,不管是救助還是略過,都避免不了經過乙個區間,就是j+1到i,所以這裡可以分而治之,把j和j+1到i這兩個分開,g[i][j]的轉移中應該需要有g[i][j+1],這裡又啟示我們進行倒序迴圈,同樣,不管救助j還是略過,從j走到j+1的這一天裡,區間j+1到i這一段的村莊都會死亡(為村民默哀?)所以答案累加sum(j+1,i)這個可以由字首和o(1)求出,到了點j+1後,j+1到i的死亡人數就已經被記在了g[i][j+1]裡,所以可以不用考慮,這是兩種情況所共同具有的死亡人數,下面對兩種情況分開討論,如果救治j的人,那麼區間j+1,i的村民就要多死一天,即sum(j+1,i),不救治呢?因為同樣的我們跑路的代價都記錄在了g[i][j+1]裡邊,所以不救治的代價就是在這段時間裡j村死亡的人數,你可能問,別的村難道沒有死亡的嗎?當然可能會有,但我們已經記錄了,所以這裡不需要再次加入,首先算一下從j跑到i再跑回來所需要的時間,這裡舉個例子,從4到5要1天,4到6要2天,4到7要3天,所以顯然跑路時j村死亡的人是2*(i-j)*a[j],2是跑了兩遍,i-j是剛剛推出來的,a[j]是j村日死亡人數,那只有這些嗎?當然不是,這只是跑路的代價,根據定義和題意,j+1到i這些村莊均被**且均在略過j後被**,所以乙個村莊一天,一共就是(i-(j+1)+1)*a[j]天,於是我們的g[i][j]就有了轉移方程

g[i][j]=g[i][j+1]+sum(j+1,i)+min(3*(i-j)*a[j],sum(j+1,i))

輔助進行轉移的方程有了之後我們就可以進行dp的轉移了,沒錯,下邊還有很噁心的算時間。

對於每前i個村莊,都不可能直接求出他的最小值,所以要列舉中間點j,即我**了前j個村莊,但是j+1被略過了,所以j+1的**是從j+1走到i再走回來時才被**,這一段的代價就是g[i][j+1],**前j個的代價為dp[j],直接累加答案即可,那麼最硬核的東西就是i+1到n的這段區間,因為在當前階段,這段區間內的人是不可能被**的,所以一天內的死亡人數是sum(i+1,n),天數呢?根據我之前所推導的,從j+1到i之間反覆橫跳一來一回一來需要天數3*(i-(j+1)),**區間j+1到i需要時間(i-(j+1)+1),這裡不要忽略了乙個地方,就是從j跑路跑到j+1時還有一天,所以總天數就是3*(i-(j+1))+i-(j+1)+1+1,乘上每天死亡的人數就是最後的代價,累加答案。

至此,這道省選題就落下了帷幕……什麼?你問我最後轉移的時候沒考慮略過i就是一路向右的情況?怎麼可能,當我列舉到j=i-1的時候,就相當於轉移了這種情況,是吧,所以這個演算法是沒有問題的,時間複雜度大致為o(n^2)可以a掉

tips::如果你實在看不懂時間怎麼算的請拿起筆自己模擬一下吧,很快就能懂,我也盡力了。

1 #include2 #include3

#define ll long long

4using

namespace

std;

5const

int n=3e3+10;6

ll s[n],g[n][n],dp[n],a[n];

7 ll sum(int l,int

r)10

intmain()

16for(int i=1;i<=n;i++)

17for(int j=i-1;j;j--)

18 g[i][j]=g[i][j+1]+sum(j+1,i)+min(3ll*(i-j)*a[j],sum(j+1

,i));

19 memset(dp,0x3f,sizeof

dp);

20 dp[0]=0;21

for(int i=1;i<=n;i++)

22for(int j=0;j)

23 dp[i]=min(dp[i],dp[j]+g[i][j+1]+sum(i+1,n)*((i-(j+1))*3+i-(j+1)+2

));24 cout<

25 }

JSOI2016病毒感染

1.題目大意 有1 n的村莊,每個村莊在不 的情況下每天死 a i 人,到達乙個村莊可以 或跳過,若跳過,再回頭時只能一直走回這個村莊,然後才能重新往前走,求最少死亡人數。2.題目分析 我們定義f i 為前 i 個村莊全部治好的最小代價。令j i,在 1 i 內列舉回頭點,因此我們再定義乙個 g i...

P5774 JSOI2016 病毒感染

題目鏈結 有 n 個小鎮爆發了疫情,其中第 i 個小鎮每天會死 a i 個人,現在從第乙個小鎮出發,每一天可以選擇 求最少死亡人數 n 3000,a i 10 9 可以發現,每個村莊只可能在第一次被經過或第二次被經過時 被 換句話說,在區間 l,r 進行一次往返走 l rightarrow r ri...

B 病毒感染

有一天clccle和rqy走在某個國家的街頭上,機智的rqy卻發現周圍的行人不太對勁,他們嘴裡念念有詞,說著 sqn tql 一邊漫無目的的行走,clccle也發現了這一點,卻驚訝的發覺這種奇怪的病毒會向周圍的城市,最終會感染整個國家,因為網路已經崩潰,所以她們忘記了自己所在的城市,她們唯一知道的是...