1. 程式人生 > >ssl1597-石子合併問題【區間dp練習】

ssl1597-石子合併問題【區間dp練習】

Description
  在一個圓形操場的四周擺放著n 堆石子。現要將石子有次序地合併成一堆。規定每次只能選相鄰的2 堆石子合併成新的一堆,並將新的一堆石子數記為該次合併的得分。試設計一個演算法,計算出將n堆石子合併成一堆的最小得分和最大得分。
程式設計任務:
  對於給定n堆石子,程式設計計算合併成一堆的最小得分和最大得分。


Input

輸入包括多組測試資料,每組測試資料包括兩行。

第1 行是正整數n,1<=n<=100,表示有n堆石子。
第2行有n個數,分別表示每堆石子的個數。



Output
對於每組輸入資料,輸出兩行。

第1 行中的數是最小得分;第2 行中的數是最大得分。


Sample Input

4
4 4 5 9

Sample Output
43

54

解題思路

  看這題之前請補一下:http://blog.csdn.net/mr_wuyongcong/article/details/78815773

  這道題是石子合併的升級版,這裡把改成了圓形,那麼說明第一堆和最後一堆也可以合併,這道題我用了一種不同的方法。可以先把所有兩個的和在一起,再把所有的三個的合在一起......以此類推。然後列出動態轉移方程:

  mins=min(mins,f[i][k]+f[k+1][j]+s[j]-s[i-1])
  maxs=max(maxs,f1[i][k]+f1[k+1][j]+s[j]-s[i-1])

還是程式碼講的清楚

程式碼

#include<cstdio>
#include<iostream>
using namespace std;
int n,x,s[201],f[201][201],f1[201][201],maxs,mins,a[201];
int main()
{
  scanf("%d",&n);
  for (int i=1;i<=n;i++)
  {
    scanf("%d",&a[i]);
    a[n+i]=a[i];//環狀相連
  }
  for (int i=1;i<=2*n;i++)
  s[i]=s[i-1]+a[i];//預處理不解釋
  for (int ii=2;ii<=n;ii++)//如我所說↑


    for (int i=1;i<=2*n-ii+1;i++)//列舉開頭
    {
int mins=2147483647,maxs=0,j=i+ii-1;//如我所說↑
for (int k=i;k<j;k++)//列舉分裂點
{
 mins=min(mins,f[i][k]+f[k+1][j]+s[j]-s[i-1]);
  maxs=max(maxs,f1[i][k]+f1[k+1][j]+s[j]-s[i-1]);

  //動態轉移方程
}
f[i][j]=mins;//最小值存入
f1[i][j]=maxs;//最大值存入
    }
  int mins=2147483647,maxs=0;
  for (int i=1;i<=n;i++)
  {
    maxs=max(maxs,f1[i][i+n-1]); //求每個區域的最大值
    mins=min(mins,f[i][i+n-1]); //求每個區域的最小值
  }
  printf("%d\n%d",mins,maxs);//get√
}