1. 程式人生 > 實用技巧 >30. 串聯所有單詞的子串

30. 串聯所有單詞的子串

題目

Description

給定一正整數序列,例如:4,1,2,3,在不改變數的位置的條件下把它們相加,並且用括號來標記每一次加法所
得到的和。例如:((4+1)+ (2+3))=((5)+(5))=10。除去原數不4,1,2,3之外,其餘都為中間結果
,如5,5,10,將中間結果相加,得到:5+5+10=20,那麼數20稱為此數列的一個代價,若得到另一種演算法:(4+
((1+2)+3))=(4+((3)+3))=(4+(6))=10,數列的另一個代價為:3+6+10=19。若給出N個數,可加N-
1對括號,求出此數列的最小代價。
注:結果範圍不超出longint.

Input

第一行為數N(1≤N≤200)

第二行為N個正整數,整數之間用空格隔開。.

Output

輸出僅一行,即為最少代價值。

Sample Input

4
4 1 2 3

Sample Output

19

思路

首先這是一道經典的dp題;

那麼d[i][j]表示i-j的最小代價,sum[i][j]是i-j每個石頭的代價;

答案就是dp[1][n]了;

那麼每一次合併i-j就需要加上i-j的權值代價;

dp[1][3]=min(dp[1][1]+dp[2][3]+sum[1][3],dp[1][2]+dp[3][3]+sum[1][3]);

所以我們需要列舉斷開的位置k,i-j的合併就是i-k的合併加上k+1-j的合併再加上代價;

所以轉移方程就是:

dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);

sum是字首和;

然後列舉len長度,i起點,k斷開的位置;

為什麼不是先列舉i起點,在列舉j終點呢?

for(ll i=1;i<=n;i++)

for(ll j=i+1;j<=n;j++)

for(ll k=i;k<j;k++)

假如求出dp[1][2],然後列舉到dp[1][3]=dp[1][1]+dp[2][3] ;

然鵝dp[2][3]還未求出,所以我們需要列舉區間長度;

從區間長度小列舉到區間長度大,這樣算長度大的區間時,訪問小區間,小區間就有答案;

所以要列舉len長度,i起點,k斷開的位置;

程式碼

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
inline ll read()
{
    ll a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}
ll n;
ll dp[2001][2001],a[2001],s[2001];
int main()
{
    n=read();
    for(ll i=1;i<=n;i++)
        a[i]=read();
    for(ll i=1;i<=n;i++)//計算字首
        s[i]=s[i-1]+a[i];
    for(ll len=2;len<=n;len++)//列舉區間長度,len=1沒什麼意義額
    for(ll i=1;i<=n-len+1;i++)
    {
        ll j=i+len-1;
        dp[i][j]=1<<30;//因為答案是求最小值,但是陣列一開始都為0,答案也會為0,所以我們需要統計時改為最大值
        for(ll k=i;k<j;k++)
            dp[i][j]=min(dp[i][k]+dp[k+1][j]+s[j]-s[i-1],dp[i][j]);//轉移方程
    }
//    for(ll i=1;i<=n;i++)
//    for(ll j=i+1;j<=n;j++)
//    {
//        dp[i][j]=1<<30;
//        for(ll k=i;k<j;k++)//假如求出dp[1][2],然後列舉到dp[1][3]=dp[1][1]+dp[2][3] 
//        {                 //dp[2][3]還未求出,所以我們需要列舉區間長度 
//            dp[i][j]=min(dp[i][k]+dp[k+1][j],dp[i][j])+s[j]-s[i-1];
//            cout<<dp[i][j]<<endl;
//        }
//    }
//不要在意上面隱了的程式碼
    printf("%lld\n",dp[1][n]);
}

寫的不好,多多諒解