首先看題:
有n堆石子排成一列,每堆石子有一個重量w[i], 每次合並可以合併相鄰的兩堆石子,一次合併的代價爲兩堆石子的重量和w[i]+w[i+1]。問安排怎樣的合併順序,能夠使得總合並代價達到最小。
設狀態f(i,j),1<=i<=j<=n表示從第i堆到第j堆所需要的最小代價。
找規律 a[i,j]表示i到j堆的數量
f(1,1)=w[1]
f(1,2)=w[1]+w[2]=f(1,1)+f(2,2) //沒有+a[1,2],因爲這只是合併了2->1個堆,自己畫圖
f(2,2)=w[2]
f(1,3)=min{f(1,1)+f(2,3),f(1,2)+f(3,3), f(1,3)+f(3,3)}+a[1,3] //想想,爲什麼要加上。畫圖
f(2,3)=w[2]+w[3]=f(2,2)+f(3,3)
f(3,3)=w[3]
f(1,4)=min{f(1,1)+f(2,4), f(1,2)+f(3,4), f(1,3)+f(3,4),f(1,4)+f(4,4)}
.......
統一一下f(i,i)即將它初始化爲0,不然值就會多加了一倍TAT。然後可以發現:
f(i,j)=min{f(i,j), f(i,k)+f(k+1,j)}+a[i,j] (i<=k<=j-1, 1<=i<=j<=n)
a[i,j]可以用dp在O(n)的時間內得到,設一個一維數組
sum[i]=sum[i-1]+w[i] (2<=i<=n)
sum[1]=a[1]
a[i,j]就相當於sum[j]-sum[i-1]
這時候方程就是
f(i,j)=min{f(i,j), f(i,k)+f(k+1,j)}+sum[j]-sum[i-1](,i<=k<=j-1, 1<=i<=j<=n)
咱們來畫圖。。(矩陣)
用一個二維數組F[i][j]表示f(i,j)。在矩陣上行的表示i,列表是j(爲了方便)
如圖:
由此可知道順序。。則遞推程序如下。(我不要下標0了,從1開始,方便,而且-1不會越界)
for(j = 1; j <= n; j++)
for(i = j; i > 0; i--)
for(k = i; k < j; k++)
f[i][j] = min(f[i][j], f[i][k]+f[k+1][j]+sum[j]-sum[i-1]);
j可以看成圖中的i, i則是j。f[i][j]即從i堆到j堆所需最小的代價。
初始化很重要= =。 非常,非常= =。。。。要將f[i][i]初始化爲0,其中1<=i<=n
並且將f[i][j]都要初始化爲INF(一個很大很大的值),否則會出錯。
因爲是從下標1開始,下標0的自然要初始化爲0,如果聲明的是全局變量一般都會清0的,這一步可以省略。
放上完整代碼= =。
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 120;
const int INF = 1000000000;
int w[MAXN], n, f[MAXN][MAXN], sum[MAXN];
int i, j, k;
int main()
{
cin >> n;
for(i = 1; i <= n; i++) for(j = 1; j <= n; j++) f[i][j] = INF;
for(i = 1; i <= n; i++) {cin >> w[i]; f[i][i] = 0; sum[i] += sum[i-1]+w[i];}
for(j = 1; j <= n; j++)
for(i = j; i > 0; i--)
for(k = i; k < j; k++)
f[i][j] = min(f[i][j], f[i][k]+f[k+1][j]+sum[j]-sum[i-1]);
cout << f[1][n] << endl;
return 0;
}