時間限制:1.0s 內存限制:256.0MB
問題描述
題目很簡單,給出N個數字,不改變它們的相對位置,在中間加入K個乘號和N-K-1個加號,(括號隨便加)使最終結果盡量大。因為乘號和加號一共就是N-1個了,所以恰好每兩個相鄰數字之間都有一個符號。例如:
N=5,K=2,5個數字分別為1、2、3、4、5,可以加成:
1*2*(3+4+5)=24
1*(2+3)*(4+5)=45
(1*2+3)*(4+5)=45
……
N=5,K=2,5個數字分別為1、2、3、4、5,可以加成:
1*2*(3+4+5)=24
1*(2+3)*(4+5)=45
(1*2+3)*(4+5)=45
……
輸入格式
輸入文件共有二行,第一行為兩個有空格隔開的整數,表示N和K,其中(2<=N<=15, 0<=K<=N-1)。第二行為 N個用空格隔開的數字(每個數字在0到9之間)。
輸出格式
輸出文件僅一行包含一個整數,表示要求的最大的結果
樣例輸入
5 2
1 2 3 4 5
1 2 3 4 5
樣例輸出
120
樣例說明
(1+2+3)*4*5=120
思路:提示說按照dp處理,就往這邊想了,先確定了dp[i][j]表示前i個數中使用了j個乘號的所得到最大值。
糾結於插入的位置,所以只好再設一重循環,表示可以插入的位置,范圍從2到n。
動態轉移方程是根據01背包的思想轉化得來的,選定插入位置,前面的最大值乘后邊從插入位置到i之前的總和,原來想的是只乘一個數,但是一起乘總比一個去乘要大。
所以狀態轉移方程:dp[i][j]=max(dp[i][j],dp[p-1][j-1]*(p到i之間的總和));
最后就是初始狀態的考慮,dp[i][0]表示沒有用到乘號,所以dop[i][0]就是前i項的和。
代碼:
#include<iostream> #include<algorithm> using namespace std; long long dp[16][16]; long long ans[16]; void init(int n,int k) { long long num; long long sum=0; for(int i=1;i<=n;i++) { scanf("%lld",&num); ans[i]=num; sum+=num; dp[i][0]=sum;//在沒有使用乘號的情況全部使用加法 } } int main() { int n,k; scanf("%d%d",&n,&k); init(n,k); for(int i=1;i<=n;i++)//n個數 { for(int j=1;j<=i-1;j++)//最多有i-1個乘號,數量級較小,就不剪枝了 { for(int p=2;p<=i;p++)//第j個乘號插入的位置,如果和前面的乘號位置重疊了,也不影響,還是原來的dp[i][j] { dp[i][j]=max(dp[i][j],dp[p-1][j-1]*(dp[i][0]-dp[p-1][0]));//從車如位置到i的和,一起乘總比一個乘要大 } } } printf("%lld\n",dp[n][k]); return 0; }