1. 程式人生 > >輸出矩陣連乘所有的完全加括號形式

輸出矩陣連乘所有的完全加括號形式

例如說,有四個矩陣ABCD相乘,那麼所有的完全加括號結果為(A((BC)D)) (A(B(CD))) ((AB)(CD)) (((AB)C)D) (A(BC))D)。要求輸出n個矩陣連乘所有的完全加括號結果。

解決方法一

  1. 基本思想:首先從1,2,3,4個矩陣的角度來歸納。如果只有一個矩陣A,那麼很明顯所有的完全加括號結果就是A本身。如果有兩個矩陣A,B相乘,很明顯所有的完全加括號結果就是AB。如果有三個矩陣A,B,C相乘,那麼我們將考慮將矩陣鏈斷開,分為兩個子矩陣鏈,然後分別求取兩個子矩陣鏈的所有完全加括號結果的集合,最後再求出這兩個集合的笛卡兒積即可。此時三個矩陣共有兩種斷開方式:A,BC和AB,C。第一種斷開方式中,左子矩陣鏈為A,該子矩陣鏈的所有完全加括號結果的集合已知為{A},右子矩陣鏈為BC,該子矩陣鏈的所有完全加括號結果的集合已知為{BC}。那麼該斷開方式的完全加括號結果為A(BC)。由此類推另外一種斷開方式的完全加括號結果為(AB)C。所以,三個矩陣相乘的所有完全加括號結果為{A(BC),(AB)C}。如果有四個矩陣A,B,C,D相乘,斷開方式有:A,BCD和AB,CD和ABC,D。第一種斷開方式中,左子矩陣鏈為A,該子矩陣鏈的所有完全加括號結果的集合已知為{A},右子矩陣鏈為BCD,從前面分析可知其所有完全加括號結果為{B(CD),(BC)D},該斷開方式的所有完全加括號結果為:A(B(CD))和A((BC)D),以此類推下去,我們可以發現n個矩陣劃分的子矩陣鏈均可以在1~n-1個矩陣中找到對應的矩陣鏈。因此,要求出n個矩陣相乘的所有完全加括號結果,可以先從1個矩陣入手,求出這n個矩陣每一個矩陣的所有完全加括號結果並儲存起來,再計算這n個矩陣每兩個相鄰矩陣的所有完全加括號結果並儲存起來,再計算這n個矩陣每三個相鄰矩陣的所有完全加括號結果並儲存起來,以此類推,最後計算出這n個矩陣每n個相鄰矩陣的所有完全加括號結果即n個矩陣相乘的所有完全加括號結果。

  2. 基本過程:假設矩陣鏈為A_1 A_2… A_n, i.求出矩陣鏈每一個矩陣的所有完全加括號結果:A_1:{ A_1},A_2:{ A_2} ,…,A_n:{ A_n} ii.求出矩陣鏈每兩個矩陣的所有完全加括號結果:A_1 A_2:{ A_1 A_2},A_2 A_3:{ A_2 A_3},…,A_(n-1) A_n:{ A_(n-1) A_n} iii.求出矩陣鏈每三個矩陣的所有完全加括號結果:A_1 A_2 A_3: { A_1 (A_2 A_3),(A_1 A_2)A_3},A_2 A_3 A_4: { A_2 (A_3 A_4), (A_2 A_3)A_4},…,A_(n-2) A_(n-1) A_n: { A_(n-1) (A_(n-1) A_n), (A_(n-2) A_(n-1))A_n} … … … 最後得到矩陣鏈的所有完全加括號結果。裡面有一些細節值得注意,就是如何找到特定矩陣鏈的所有完全加括號結果?在這裡我用了unordered_map容器,將特定的矩陣鏈作為鍵值,將該矩陣鏈的所有完全加括號結果集合作為對映值,這樣就能在較短時間內找到想要的結果。

  3. 程式碼

#include<iostream>
#include<vector>
#include<string>
#include<unordered_map>
#include<ctime>
using namespace std;
clock_t start,end_t;

int main()
{
	cout << "輸入矩陣相乘的個數:";
	int n;
	cin >> n;

	cout << n <<"個元素的矩陣序列如下:";
	string str1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ", str2 = str1.substr(0, n);
	cout << str2<< endl;

	cout << n << "個矩陣相乘的所有完全加括號結果:" << endl;
	start = clock();
	if (n == 1)
		cout << "A" << endl;
	else if (n == 2)
		cout << "AB" << endl;
	else if (n == 3)
		cout << "(AB)C\nA(BC)" << endl;
	else
	{
		unordered_map<string, vector<string>> record;
		for (int i = 0; i < n; i++)
		{
			string t1(1,str2[i]);
			record[t1] = {t1}; //儲存一個矩陣的所有完全加括號結果 
		}
		for (int i = 0; i < n-1; i++)
		{
			string t2(2, 'x');
			t2[0] = str2[i];
			t2[1] = str2[i + 1];
			record[str2.substr(i, 2)] = {t2}; //儲存每兩個相鄰矩陣的所有完全加括號結果 
		}
		for(int t = n -2; t >= 1; t--)
		{
			for (int i = 0; i < t; i++)
			{
				string t3 = str2.substr(i, n - t + 1); //取出矩陣鏈每n-t-1個相鄰矩陣(3-n) 
				for (int j = 1; j < n - t + 1; j++)
				{
					string t4 = t3.substr(0, j); //斷開 
					string t5 = t3.substr(j, n - t - j + 1);
					int count1 = record[t4].size(), count2 = record[t5].size();
					for (int k1 = 0; k1 < count1; k1++) //求出兩個集合的笛卡兒積 
					{
						for (int k2 = 0; k2 < count2; k2++)
						{
							string t6;
							if (record[t4][k1].size() == 1)
								t6 += record[t4][k1];
							else
								t6 += "(" + record[t4][k1]+")";
							if (record[t5][k2].size() == 1)
								t6 += record[t5][k2];
							else
								t6 += "(" + record[t5][k2] + ")";
							record[t3].push_back(t6);
						}
					}
				}
			}
			//t--;
		}
		end_t = clock();
		double endtime = (double)(end_t-start)/CLOCKS_PER_SEC;
		cout << "Total time:" << endtime << endl; 
		system("pause");
		vector<string>res = record[str2];
		for (string i : res)
			cout << i << endl;
		cout << "一共有" << res.size() << "種可能的輸出." << endl;
	}
	return 0;
}

解決方法二 1.基本思想:如果題目的要求是輸出有多少種矩陣連乘的完全加括號的形式,那麼有以下實現方法:

//n表示矩陣鏈的矩陣數目
int f(int n)
{
	int num=0;
	if (n == 1)
		num = 1;
	else
		for (int i = 1; i != n; i++)
			num += f(i)*f(n - i); //左子矩陣鏈完全加括號的數目乘以右子矩陣鏈完全加括號的數目
	return num;
}

那麼我們也可以考慮用這種方式來進行輸出。

  • 若n=1,那麼直接輸出A(假設該矩陣為A)
  • 若n=2,那麼直接輸出(AB)(j假設該矩陣鏈為AB)
  • 若n>2,那麼可以將矩陣鏈分為左子矩陣鏈和右子矩陣鏈,然後分別求左子矩陣鏈的所有完全加括號結果和右子矩陣鏈的所有完全加括號結果,最後再求他們的笛卡兒積。

2.程式碼

#include<iostream>
#include<vector>
#include<string>
using namespace std;

vector<string> matrix_chain(string * p, int start, int length)
{
	vector<string> ret;
	if (length == 1) {
		ret.push_back(p[start]);
	}
	else if (length == 2) {
		ret.push_back("(" + p[start] + p[start + 1] + ")");
	}
	else {
		for (int i = 1; i != length; i++) {
			vector<string> part1 = matrix_chain(p, start, i);
			vector<string> part2 = matrix_chain(p, start + i, length - i);
			for (int k = 0; k != part1.size(); k++) {
				for (int m = 0; m != part2.size(); m++) {
					ret.push_back("(" + part1[k] + part2[m] + ")");
				}
			}
		}
	}
	return ret;
}

int main()
{
	int n;
	cout << "請輸入矩陣個數:";
	cin >> n;
	string* p = new string[n];  // 向量p用來儲存n個矩陣
	for(int i=0; i!=n; i++)
	{
		p[i] = char(i + char('A'));
	}
	vector<string> result = matrix_chain(p, 0, n);
	cout << "理論上,方案數量:" << f(n) << endl;
	cout << "矩陣鏈乘完全括號化方案數量:"<<result.size() << endl;
	for (int i = 0; i != result.size(); i++)
		cout << result[i] << endl;

	return 0;
}