【wikioi】1017 乘積最大

題目鏈接

算法:劃分型DP


非常典型的一道題目,劃分型DP

題目描述:

設有一個長度爲N的數字串,要求選手使用K個乘號將它分成K+1個部分,找出一種分法,使得這K+1個部分的乘積能夠爲最大。

同時,爲了幫助選手能夠正確理解題意,主持人還舉了如下的一個例子:

有一個數字串:312, 當N=3,K=1時會有以下兩種分法:

1)  3*12=36

2)  31*2=62

這時,符合題目要求的結果是:31*2=62

現在,請你幫助你的好朋友XZ設計一個程序,求得正確的答案。


設數字串爲a1a2a3……an。當k=1時,最大值爲

max{a1*a2a3……an,  a1a2*a3……an,  ……  ,  a1a2a3……an-1*an}

當k=2時,最大值爲

max{a1*a2*a3……an,  a1*a2a3……*an,  ……  ,  a1a2a3……*an-1*an}

引入記號f[i,k]表示從a0到ai,插入k個乘號所取得的最大值,用c[i,j]表示從ai到aj的數字列,則:

K=1時

f[n,1]=max{c[1,1]*c[2,n],  c[1,2]*c[3,n],  ……  ,  c[1,n-1]*c[n,n]}

K=2時

f[n,2]=max{f[n-1,1]*c[n,n],  f[n-2,1]*c[n-1,n],  ……  ,  f[2,1]*c[3,n]}

所以導出

f[n,k]=max{f[n-1,k-1]*c[n,n],  f[n-2,k-1]*c[n-1,n],  .......  , f[k,k-1]*c[k+1,n]}


我們用F[n][k]來表示f[n,k],表示劃分k次得到的數最大,用A[i][j]表示c[i,j]

得到:

F[i][1] = max(F[i][1], A[1][j]*A[j+1][i])  (1 <= j < i)

F[i][k] = max(F[i][k], A[j+1][i]*F[j][k-1]) (k <= j < i)

其實這裏可以簡化成:

F[i][0] = A[1][i]  (1 <= i <= n)

F[i][k] = max(F[i][k], A[j+1][i]*F[j][k-1]) (k <= j < i, 1 <= k <= m) m是要添加的乘號數目


而且發現,方程是以劃分次數k爲階段,且順序是遞增(從k到i枚舉j即可),那麼我們就自底向上的來遞推

所以順序就一木瞭然了

上代碼:

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

int n, m, i, j, k;
const int MAXK = 10;
const int MAXN = 100;
int c[MAXN] = {0}, A[MAXN][MAXN] = {{0,0}}, F[MAXN][MAXK] = {{0,0}};
int makeConut(int x, int y)   //求x到y之間的數字列
{
	int ans = 0;
	while(x <= y) ans = ans * 10 + c[x++];
	return ans;
}

int main()
{
	string str;
	cin >> n >> m;
	cin >> str;
	for(i = 1; i <= n;i++) c[i] = (str[i-1]-'0');
	for(i = 1; i <= n; i++)
		for(j = 1; j <= n; j++)
			A[i][j] = makeConut(i, j);  //初始化A數組
	//初始化k=0時的情況
	//F[i][0] = A[1][i]  (1 <= i <= n)
	for(i = 1; i <= n; i++)
		F[i][0] = A[1][i];
	//DP
	//F[i][k] = max(F[i][k], A[j+1][i]*F[j][k-1]) (1 <= k <= m)
	for(k = 1; k <= m; k++)
		for(i = k+1; i <= n; i++)
			for(j = i-1; j >= k; j--)
				F[i][k] = max(F[i][k], A[j+1][i]*F[j][k-1]);
	cout << F[n][m] << endl;
	return 0;
}





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