1. 程式人生 > >蒟蒻吃藥計劃-治療系列 #round 2 合並石子+乘積最大

蒟蒻吃藥計劃-治療系列 #round 2 合並石子+乘積最大

事情 sca 最大 一個 lin warn fine ext gif


1.合並石子

《信息學奧賽一本通》第五版 P371 第三節 T1

我就直接開始講吧:

Warning:這個題目和 合並果子 不一樣!不一樣!不一樣!不一樣!不一樣!不一樣!不一樣!不一樣!

技術分享圖片:我想告訴你一個事情,你幫幫我好麽?

(內心:mmp怎麽又是這個人)

技術分享圖片:昨天我去商場的時候,錢包被偷了,銀行卡啥的都沒了,你能幫幫我麽?

(內心:憑啥,我就不幫)

技術分享圖片:如果你幫我找到的話,我給你50金幣好不好?

(*聽到這句話,你充滿了決心)

好吧,那我們幫幫他吧,讓我們先看看他遇到了什麽問題

技術分享圖片:那個小偷在我的錢包裏留了一張紙條,上面寫著一個地址,我昨天到那裏去之後看到那兒有幾堆石頭,上面都有數字,旁邊有一塊石碑,要我把相鄰的兩對合並,最後只剩一堆,讓花費的體力最小

體力是怎麽計算的呢?

技術分享圖片:兩堆石子合並之後,花費的體力值為兩堆石頭上的數字的和

好,那我們先和他告別,自己想一想

他已經告訴我們,有 N 堆石子,每堆石子都有對應的一個數字

我們就開一個 stone 數組表示吧

不過要說明一點,為了表示方便,我們把 stone[i] 表示為前 i 堆石子的和

我們再開一個 giver 二維數組,並且讓 giver[i][j] 表示為第 i 堆到第 j 堆的石子合並之後花費的最小體力值

那麽,我們應該怎麽去求得花費最小的體力值呢?

讓我們仔細思索一下

先開個循環吧

設有一 i 並使得 i=n-1...1 作為左端點

再設一 j 使得 j=i+1...n

作為右端點

可是這樣好像還不夠

那我們再弄一個 k 並讓 k=i...j-1i 開頭 j 結尾的 giver[i][j] 分成兩段

k 枚舉每一種可能的分段,一步一步推出正確答案!

根據上面的東西,我們就可以推出狀態轉移方程:

giver[i][j]=min(giver[i][j],giver[i][k]+giver[k+1][j]+stone[j]-stone[i-1]);

對啦!

邊界條件:

giver[1...n][1...n]=0;

現在,讓我們帶著這兩個小朋友開始破譯這個問題吧!

代碼如下:

技術分享圖片
 1 #include <bits/stdc++.h>
 2
#define fp(i,l,r) for(register int i=(l);i<=(r);++i) 3 #define fd(i,l,r) for(register int i=(l);i>=(r);--i) 4 using namespace std; 5 inline int botposs(int a,int b,int pd){ 6 if(pd==1) return a>b?a:b; 7 if(pd==0) return a>b?b:a; 8 } 9 int main(){ 10 int n; 11 int stone[100+20]={0}; 12 int giver[100+20][100+20]; 13 memset(giver,32,sizeof(giver)); 14 stone[0]=0; 15 scanf("%d",&n); 16 fp(i,1,n){ 17 int x; 18 scanf("%d",&x); 19 stone[i]=stone[i-1]+x; 20 } 21 fp(i,1,n){ 22 giver[i][i]=0; 23 } 24 fd(i,n-1,1){ 25 fp(j,i+1,n){ 26 fp(k,i,j-1){ 27 giver[i][j]=botposs(giver[i][j],giver[i][k]+giver[k+1][j]+stone[j]-stone[i-1],0); 28 } 29 } 30 } 31 printf("%d",giver[1][n]); 32 return 0; 33 }
合並石子

現在我們去找他吧!

他成功了嗎?

技術分享圖片:成是成功了,錢包也找回來了,可是解開這個謎題之後,突然出現了一個山洞,裏面有一個房間,烏七八黑的,我不敢進去,你再幫幫我好嗎?如果成功,我給你100金幣

(*你充滿了決心)

好吧,我們就再答應他一次吧!


未完待續……

蒟蒻吃藥計劃-治療系列 #round 2 合並石子+乘積最大