利用窮舉法求解法插入4個乘號請訪問:http://www.vcworld.net/bbs/viewthread.php?tid=8580&extra=
一、問題提出
在一個由n個數字組成的數字串中插入r個乘號(1<=r<n<16),將它分成r+1個整數,找出一種乘號的插入方法,使得r+1個整數的乘積最大。
例如,對給定的數串847313926,如何插入r=5個乘號,使其積最大?
二、動態規劃設計
對於一般插入r個乘號,採用窮舉已不適合。注意到插入r個乘號是一個多階段決策問題,應用動態規劃來求解是適宜的。
1.建立遞推關係
設f(i,k)表示在前i位元中插入k個乘號所得乘積的最大值,a(i,j)表示從第i個數字到第j個數字所組成的j-i+1(i<=j)位整數值。
為了尋求遞推關係,先看一個執行個體:對給定的847313926,如何插入r=5個乘號,使其乘積最大?我們的目標是為了求取最優值f(9,5)。
①設前8個數字中已插入4個乘號,則最大乘積為f(8,4)*6;
②設前7個數字中已插入4個乘號,則最大乘積為f(7,4)*26;
③設前6個數字中已插入4個乘號,則最大乘積為f(6,4)*926;
④設前5個數字中已插入4個乘號,則最大乘積為f(5,4)*3926;
比較最大值即為f(9,5)。
依此類推,為了求f(8,4):
①設前7個數字中已插入3個乘號,則最大乘積為f(7,3)*2;
②設前6個數字中已插入3個乘號,則最大乘積為f(6,3)*92;
③設前5個數字中已插入3個乘號,則最大乘積為f(5,3)*392;
④設前4個數字中已插入3個乘號,則最大乘積為f(4,3)*1392;
比較以上4個數值的最大值即為f(8,4)。
一般地,為了求取f(i,k),考察數字串的前i個數字,設前j(k<=j<i)個數字中已插入k-1個乘號的基礎上,在第j個數字後插入第t個乘號,顯然此時的最大乘積為f(j,k-1)*a(j+1,i)。於是可以得遞推關係式:
f(i,k)=max(f(j,k-1)*a(j+1, i)) (k<=j<i)
前j個數字沒有插入乘號時的值顯然為前j個數字組成的整數,因而得邊界值為:
f(j,0)=a(1,j) (1<=j<=i)
為簡單計,在程式設計中省略a數組,用變數d替代。
2.遞推計算最優值
for (d = 0, j = 1; j <= n; j++) {<br />d = d * 10 + b[j - 1];// 輸入數字串每一位賦值給b數組<br />f[j][0] = d;// 計算初始值f[j][0]<br />}</p><p>for (k = 1; k <= r; k++)<br />{<br />for (i = k + 1; i <= n; i++)<br />for (j = k; j < i; j++) {<br />for (d = 0, u = j + 1; u <= i; u++)<br />d = d * 10 + b[u - 1];// 計算d即為a(j+1,i)<br />if (f[i][k] < f[j][k - 1] * d)// 遞推求取f[i][k]<br />f[i][k] = f[j][k - 1] * d;<br />}<br />}
3.構造最優解
為了能列印相應的插入乘號的乘積式,設定標註位置的數組t(k)與c(i,k),其中c(i,k)為相應的f(i,k)的第k個乘號的位置,而t(k)標明第k個乘號“*”的位置,例如,t(2)=3,表示第2個“*”號在第3個數字後面。
當給數組元素賦值f(i,k)=f(j,k-1)*d時,作相應賦值c(i,k)=j,表示f(i,k)的第k個乘號的位置是j。在求得f(n,r)的第r個乘號位置t(r)=c(n,r)=j的基礎上,其他t(k) (1<=k<=r-1)可應用下式逆推產生:
t(k)=c(t(k+1),k)
根據t數組的值,可直接按字元形式列印表面積所求得的插入乘號的乘積式。
三、程式實現
// 在一個數中插入r個乘號,使其積最大<br />// 利用動態規劃求解<br />#include <stdio.h><br />#include <string.h></p><p>int main()<br />{<br />char numStr[16];<br />int i, j, k, len, u, r, b[16], t[16], c[16][16];<br />double f[17][17], d;</p><p>printf("請輸入整數:");<br />scanf("%s", numStr);<br />printf("請輸入插入的乘號個數r: ");<br />scanf("%d", &r);<br />len = strlen(numStr);<br />if (len <= r) {<br />printf("輸入的整數位夠數不夠或r太大!/n");<br />return 0;<br />}</p><p>printf("在整數%s中插入%d個乘號,使乘積最大:/n", numStr, r);<br />for (d = 0, j = 0; j <= len - 1; j++)<br />b[j] = numStr[j] - '0';<br />for (d = 0, j = 1; j <= len; j++) {<br />d = d * 10 + b[j - 1];// 把b數組的一個字元轉化為數值<br />f[j][0] = d;// f[j][0]賦初始值<br />}</p><p>for (k = 1; k <= r; k++)<br />for (i = k + 1; i <= len; i++)<br />for (j = k; j < i; j++) {<br />for (d = 0, u = j + 1; u <= i; u++)<br />d = d * 10 + b[u - 1];<br />if (f[i][k] < f[j][k - 1] * d) {<br />f[i][k] = f[j][k - 1] * d;<br />c[i][k] = j;<br />}<br />}</p><p>t[r] = c[len][r];<br />for (k = r - 1; k >= 1; k--)<br />t[k] = c[t[k + 1]][k];// 逆推出第k個*號的位置<br />t[0] = 0; t[r + 1] = len;<br />for (k = 1; k <= r + 1; k++) {<br />for (u = t[k - 1] + 1; u <= t[k]; u++)<br />putchar(numStr[u - 1]);// 輸出最優解<br />if (k < r + 1)<br />putchar('*');<br />}<br />printf("=%.0f/n", f[len][r]);// 輸出最優值</p><p>return 0;<br />}
選自《趣味C程式集錦》
// 在一個數中插入r個乘號,使其積最大<br />// 利用動態規劃求解<br />#include <stdio.h><br />#include <string.h></p><p>const int maxn = 41;<br />int n[maxn], len; </p><p>// nt...nz整數的合成<br />long nn(int t, int z)<br />{<br />int i;<br />long a = n[t];</p><p>for (i = t + 1; i <= z; i++)<br />a = a * 10 + n[i];</p><p>return a;<br />}</p><p>int main(void)<br />{<br />int i, N, c, k, t;<br />long num, f[maxn][maxn];<br />char s[maxn];</p><p>scanf("%d%d", &N, &c);<br />scanf("%s", s);<br />len = strlen(s);<br />for (i = 0; i < len; i++)<br />n[i] = s[i] - '0';<br />k = 0;<br />f[0][0] = n[0];<br />for (i = 1; i < len; i++)<br />f[i][0] = f[i - 1][0] * 10 + n[i];<br />k = 1;<br />for (k = 1; k <= c; k++) {<br />for (i = k; i < len; i++) {<br />long a = -1;<br />for (t = k - 1; t < i; t++) {<br />num = nn(t + 1, i);<br />long b = f[t][k - 1] * num;<br />if (a < b)<br />a = b;<br />}<br />f[i][k] = a;<br />}<br />}<br />printf("%d/n", f[len - 1][c]);</p><p>return 0;<br />}
選自《ACM/ICPC程式設計分析》