蒟蒻吃藥計劃-治療系列 #round 2 合並石子+乘積最大
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-1 把 i 開頭 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 合並石子+乘積最大