1. 程式人生 > >動規之刪數問題

動規之刪數問題

刪數問題

問題描述
現有n個正整陣列成的序列a,從中刪除一個數,得分是其本身同左、右相鄰的數的乘積,
然後再在剩餘的整數中繼續刪除,注意序列兩端的數字a1和an是不能刪除的,求這樣刪除n-2個整數後的最大得分。

例如有四個數3 、4、5、6,按照先4後5的刪除順序,其得分為345+356=150,
按照先5後4的刪除順序,其得分為456+346=192,因此最大得分為192。

測試樣例:

  • 第一組

    4
    3 4 5 6

  • 第二組

    5
    3 6 7 8 2

問題分析
----又是一個典型的區間動規
最優子結構:

dp[i][j] = dp[i][k] + dp[k][j] + a[i] * a[k] * a[j];

對於第i個物品到第j個物品,假設最後刪掉k得到的結果最大,那麼最後一次刪除時,得到的分數就是 a[i] * a[k] * a[j]。那麼總的得分就是需要加上之前刪掉k的左右兩邊除了i,j之外所有數的和,即dp[i][j] 的兩個子問題,分別是dp[i][k] 和dp[k][j]。由此便可得出上面所寫的最優子結構

程式碼

/*
刪數問題: 
現有n個正整陣列成的序列a,從中刪除一個數,得分是其本身同左、右相鄰的數的乘積,
然後再在剩餘的整數中繼續刪除,注意序列兩端的數字a1和an是不能刪除的,求這樣刪除n-2個整數後的最大得分。

例如有四個數3 、4、5、6,按照先4後5的刪除順序,其得分為3*4*5+3*5*6=150,
按照先5後4的刪除順序,其得分為4*5*6+3*4*6=192,因此最大得分為192。

測試樣例: 

4
3 4 5 6
 
5
3 6 7 8 2 

*/
#include<iostream> #include<cstring> #include<algorithm> using namespace std; #define MAX_N 1010 static int a[MAX_N];//用來儲存序列 static int dp[MAX_N][MAX_N];//i j 用來儲存刪除第i個數到第j個數所得到的最優值 void dp1(int n) { int j = 0; for(int r = 3;r <= n;r ++ ){ for(int i = 1;;i ++ ){ int j =
i + r -1; for(int k = i + 1;k <= j - 1;k ++ ){ dp[i][j] = max(dp[i][j],dp[i][k]+dp[k][j]+a[i]*a[k]*a[j]); } if(j >= n) break; } } cout << "刪除n-2個數之後,得到的最大值為:" << endl; cout << dp[1][n] << endl; } int main() { int n; cout << "請輸入序列的位數n" << endl; while(cin >> n,n){ for(int i = 1;i <= n;i ++ ){ cin >> a[i]; } memset(dp,0,MAX_N*MAX_N); for(int i = 1;i <= n - 1;i ++ ){ dp[i][i + 1] = 0; } dp1(n); cout << "請輸入序列的位數n" << endl; } return 0; }