1. 程式人生 > >石子合併問題——直線

石子合併問題——直線

簡單的區間dp,dp[i][j]表示從i~j區間所得的最大最小分。
以最大得分為例:
1.首先初始化dp函式,為0;
2.計算出字首和;
3.列舉區間長度len;
4.列舉起始區間的位置j;
5.因為將新的一堆石子的數量記為得分數,以dp[1][2]為例,
第一堆第二堆石子合併後數量為a[1]+a[2],即sum[2]-sum[0],
這是得分數,dp[1][2] = dp[1][1]+dp[2][2]+sum[2]-sum[0];
所以dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1])
含義是第i~k堆的石子加上k+1~j堆的石子,在合併k和k+1堆時
分數是第i~j堆石子數量的總和

石子合併問題

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int main()
{
    int dp[101][101],sum[101],a[101],n;
    while(~scanf("%d",&n))
    {
        memset(dp,0,sizeof(dp));
        memset(sum,0,sizeof(sum));
        for(int i = 1 ; i <= n ; i++)
        {
            scanf
("%d",&a[i]); sum[i] = sum[i-1]+a[i]; } for(int len = 2 ; len <= n ; len++)//列舉區間長度 { for(int i = 1 ; i <=n ;i++) { int j = i+len-1; if(j>n) continue; for(int k = i ; k < j ;k++) { dp[i][j] = max(dp[i][j],dp[i][k]+dp[k+1
][j]+sum[j]-sum[i-1]); } } } int a1 = dp[1][n]; for(int i = 0 ; i <= n ; i++) for(int j = 0 ; j <= n ; j++) dp[i][j] = 1e8; for(int i = 0 ;i<=n ;i++) dp[i][i] = 0; for(int len = 2 ; len <= n ; len++) { for(int i = 1 ; i <=n ;i++) { int j = i+len-1; if(j>n) continue; for(int k = i ; k < j ;k++) { dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]); } } } int a2 = dp[1][n]; cout<<a2<<" "<<a1<<endl; } return 0; }