石子合併問題——直線
阿新 • • 發佈:2019-02-13
簡單的區間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;
}