最大的算式(BigExp) 動態規劃

還記得是去年做的DP題目,題目大意如下:

給出N個數字,不改變它們的相對位置,在中間加入K個乘號和N-K-1個加號,(括號隨便加)使最終結果儘量大。因爲乘號和加號一共就是N-1個了,所以恰好每兩個相鄰數字之間都有一個符號。例如:

N=5, K=25個數字分別爲12345,可以加成:

1*2*(3+4+5)=24

1*(2+3)*(4+5)=45

   。。。

輸入

輸入文件共有二行,第一行爲兩個有空格隔開的整數,表示NK,其中(2<=N<=15, 0<=K<=N-1)。第二行爲 N個用空格隔開的數字(每個數字在09之間)。

 

輸出

輸出文件僅一行包含一個整數,表示要求的最大的結果

 

樣例

BIGEXP.IN

5 2

1 2 3 4 5


BIGEXP.OUT

120  // (1+2+3)*4*5=120


當時記得很清楚,老師給出的轉移方程是:

F[i][j]表示前i個數字用j個乘號所有的最大值;

F[i][j]=max{F[i-1][j]+arr[i],[F[i-k][j-1]*sum[i-k+1][i]};

當時就是得了90分(據說在各大oj上能AC)

然後班裏的大犇說:方程不對,要三維的區間DP。

去年的我實在太弱了,不提了,這幾天突然想起來,沒思考多久,代碼就想出來了。


題目不能枚舉最後一次乘法,這是不對的,遇到多的‘0’就要傻眼了,應該採用分治思想,把一段區間內一分爲二,這樣能保證得到的答案最大。


#include <iostream>
#include <cstring>
#define N 20
#define m_inf -999999
using namespace std;
int f[N][N][N]; //dp三維數組 
int sum[N][N];  //求i~j的總和 
int arr[N];     //保存數字 
int n,m;     

void reset()  //清空數組 
{
	memset(f,0,sizeof(f));
	memset(arr,0,sizeof(arr));
	memset(sum,0,sizeof(sum));
}



int search(int s,int e,int k) //記憶化搜索 
{
	if(e-s<k)return f[s][e][k]=m_inf; //如果長度小於乘號個數,返回負無窮 
	if(f[s][e][k]!=0)return f[s][e][k];  
	int &maxnum=f[s][e][k];
	if(k==0)maxnum=sum[s][e];  //乘號個數爲0時即爲求和 
	else
	for (int i=s;i<e;++i) //枚舉中間點 
	{
		
		for(int j=0;j<=k;++j)
		  maxnum=max(maxnum,search(s,i,j)+search(i+1,e,k-j)); //如果左右相加  
		for(int j=0;j<k;++j)
		  maxnum=max(maxnum,search(s,i,j)*search(i+1,e,k-j-1));//如果左右相乘 
	}
	return maxnum;
	
}
int main()
{
  while(cin>>n>>m)
  { 
  	reset();
  	for (int i=1;i<=n;++i)
  	  cin>>arr[i];
	for (int i=1;i<=n;++i)
	  for (int j=i;j<=n;++j)
        sum[i][j]+=sum[i][j-1]+arr[j];
    cout<<search(1,n,m)<<endl;
  }	
}

給一組有很多0的數據:

15 5
0 1 0 0 1 0 1 0 1 1 1 0 1 1 0

答案是12 


如果用錯誤的枚舉最後一次的方法 答案是3


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