【基礎演算法】石子合併-版本1
阿新 • • 發佈:2019-02-05
此題主要是用dp求出每個範圍的最小价值,再用遞迴輸出。
時間限制: 1 Sec 記憶體限制: 64 MB
題目描述
設有n堆石子排成一排,其編號為1,2,3,…,n。每堆石子有一定的數量,例如: 13 7 8 16 21 4 18 現要將n堆石子歸併為一堆。歸併的過程為每次只能將相鄰的兩堆石子堆成一堆,這樣經過n-1次歸併之後最後成為一堆。對於上面的7堆石子,可以有多種方法歸併成一堆。其中的2種方法入下圖: 歸併的代價是這樣定義的:將兩堆石子歸併為一堆時,兩堆石子數量的和稱為歸併2堆石子的代價。如上圖中,將13和7歸併為一堆的代價為20。歸併的總代價指的是將沙子全部歸併為一堆沙子的代價的和。如上面的2種歸併方法中, 第1種的總代價為 20+24+25+44+69+87 = 267 第2種的總代價為 15+37+22+28+59+87 = 248 由此可見,不同歸併過程得到的總的歸併代價是不一樣的。 當n堆石子的數量給出後,找出一種合理的歸併方法,使總的歸併代價為最小。
輸入
第1行:1個整數n(1<=n<=100),表示石子的數量第
2行:n個用空格分開的整數,每個整數均小於10000,表示各堆石子的數量。
輸出
第1行:1個整數,表示最小的歸併代價
第2行:用括號表示的歸併順序。加括號的要求見樣例。如果只有1堆石子,輸出時不要加括號。
樣例輸入
3
13 7 8
樣例輸出
43
(13)((7)(8))
要用dp存入的話,就先得把每個範圍的的總價值搞清楚。(每個合併下的總值都不會超過總每個範圍的石頭數)
===>
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf ("%d",&c[i]);
w[i]=w[i-1]+c[i];
}
//若要求i_j範圍內的總價值,即為w[j]-w[i-1](j>i)
然後為了便於dp先把長度定下來
for(int len=1;len<n;len++)
列舉起始點
for(int i=1;i+len<=n;i++)
然後列舉每一個端點(用於分割槽域)並存入座標。
for(int len=1;len<n;len++)
for(int i=1;i+len<=n;i++)
{
int j=i+len ;
t=INT_MAX;
for(int k=i;k<j;k++)
if(dp[i][k]+dp[k+1][j]<t)
{
pre[i][j]=k;
t=dp[i][k]+dp[k+1][j];
}
dp[i][j]=t+w[j]-w[i-1];
}
輸出
printf("%d\n",dp[1][n]);
然後用一個遞迴輸出(分範圍):
{
if(i==j)
{
printf("%d",c[i]);
return ;
}
printf("(");
print(i,pre[i][j]);
printf(")(");
print(pre[i][j]+1,j);
printf(")");
}
完整程式碼如下:
#include<cstdio>
#include<climits>
int n,c[105],w[105],dp[105][105],pre[105][105],t;
void print(int i,int j)
{
if(i==j)
{
printf("%d",c[i]);
return ;
}
printf("(");
print(i,pre[i][j]);
printf(")(");
print(pre[i][j]+1,j);
printf(")");
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&c[i]);
w[i]=w[i-1]+c[i];
}
for(int len=1;len<n;len++)
for(int i=1;i+len<=n;i++)
{
int j=i+len;
t=INT_MAX;
for(int k=i;k<j;k++)
if(dp[i][k]+dp[k+1][j]<t)
{
pre[i][j]=k;
t=dp[i][k]+dp[k+1][j];
}
dp[i][j]=t+w[j]-w[i-1];
}
printf("%d\n",dp[1][n]);
print(1,n);
}