0/1揹包問題與矩陣乘法鏈(動態規劃)

何爲0/1揹包問題:

0/1揹包爲一種特殊的揹包問題,一個固定容量C的揹包要裝N種物品,每種物品只有一個,每種物品重量和價值都不相同,且只能選擇裝進去或者不裝進去(即選擇0或者1,無法只裝一部分),要使揹包中的物品價值最大,此爲0/1揹包問題。

具體問題描述:

存在一個容量爲10的揹包,有5種物品,分別重2,2,6,5,4千克,價值分別是6,3,5,4,6元,選擇方案使揹包中物品的總價值最大。

動態規劃解題思路:

動態規劃與貪心都把大問題分解爲小問題,但會不斷記錄前面求解出的結果用於後面的求解,從而減少了時間。動態規劃的核心在於遞推,所以要找到合適的遞推公式,並且建立表格填表,把遞推公式循環完畢即求出問題的答案。

在0/1揹包問題的,一個物品只有裝與不裝兩種選擇,這時候,就出現了兩種情況:

(1)已經裝了i-1個物品,要裝第i個物品,但包裝不下了,此時MaxValue[i][j] = MaxValue[i-1][j],其中j表示揹包容量;

(2)要裝第i個物品,包容量也夠,要判斷裝下這個物品是否滿足最優解,此時判斷公式爲:

MaxValue[i][j] = max{(MaxValue[i-1][j-Weight[i]] + Value[i]) , MaxValue[i-1][j]},即裝與不裝哪個價值大。

01揹包問題代碼實現:

準備了二維數組來作爲輸出結果的存儲:

#define Num 5             //物品數量
#define Cap 10            //揹包容量

int V[Num+1] = { 0,6,3,5,4,6 };      //揹包物品價值
int W[Num+1] = { 0,2,2,6,5,4 };        //揹包物品重量
int M[Num + 1][Cap + 1] = { 0 };    //結果數組

//01揹包動態規劃
void KnaspsackProblem(int *v,int *w)
{
	int i, j;      //用於循環
	for (i = 1; i <= Num; i++)
	{
		for (j = 1; j <= Cap; j++)
		{
			if (j < w[i])
			{
				M[i][j] = M[i - 1][j];
			}
			else
			{
				int temp1 = M[i - 1][j];
				int temp2 = M[i - 1][j - w[i]] + v[i];
				M[i][j] = temp1 > temp2 ? temp1 : temp2;
			}
		}
	}
}

結果與分析:

int main()
{
	KnaspsackProblem(V, W);
	for (int i = Num; i >0; i--)
	{
		for (int j = 1; j <= Cap; j++)
		{
			cout << M[i][j] << "\t";
		}
		cout << endl;
	}
    return 0;
}

輸出爲:

i/j     1       2        3       4       5        6        7        8        9       10

5       0       6       6       9       9       12      12      15      15      15
4       0       6       6       9       9        9       10      11      13      14
3       0       6       6       9       9        9        9       11      11      14
2       0       6       6       9       9        9        9        9        9        9
1       0       6       6       6       6        6        6        6        6        6

即最大價值爲15。

何爲矩陣乘法鏈:

矩陣之間可以進行乘法操作,但必須滿足Apq * Bmn中的A矩陣的列數等於B矩陣的行數,即q = m時,此時相乘得到的矩陣C的大小爲p*n,其相乘次數爲p*q*n。

具體問題描述:

給定n個矩陣構成的一個鏈<A1,A2,A3,.......An>,其中i=1,2,...n,矩陣A的維數爲pi-1pi,對乘積 A1A2...An 以一種最小化標量乘法次數的方式進行加全部括號。

動態規劃解題思路:

遞推方程爲:

i ==j時 ,m[i][j] = 0  ;

i < j時,m[i][j] = min{m[i,k]+m[k+1,j]+pi-1-1*pk*pj。

矩陣乘法鏈代碼實現:

#define N 10
//矩陣乘法鏈
void MatrixChain(int *p,int (*m)[N],int (*t)[N],int length)
{
	int n = length - 1;       //矩陣數量
	int i, j,k;//用於循環
	int s,num;
	//當i=j時,只有一個矩陣,相乘次數爲0
	for (i = 1; i < length; i++)
	{
		m[i][i] = 0;
	}
	for (i = 2; i < length; i++)
	{
		for (j = 1; j < n - i + 1; j++)
		{
			s = j + i - 1;
			m[j][s] = 0x7fffffff;       //爲無窮值
			for (k = j; k <= s - 1; k++)
			{
				num = m[j][k] + m[k + 1][s] + p[j - 1] * p[k] * p[s];
				if (num < m[j][s])
				{
					m[j][s] = num;
					t[j][s] = s;
				}
			}
		}
	}

}
//打印結果
void OutputResult(int(*t)[N], int i, int j)
{
	if (i == j)
	{
		cout << "A" << i;
	}
	else
	{
		cout << "(";
		OutputResult(t, i, t[i][j]);
		OutputResult(t, t[i][j] + 1, j);
		cout << ")";
	}
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章