石子合併

首先看題:

有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;
}

 

發佈了40 篇原創文章 · 獲贊 2 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章