洛谷 P1880 [NOI1995]石子合并

题目描述

在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.

输入输出格式

输入格式:

数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.

 

输出格式:

 

输出共2行,第1行为最小得分,第2行为最大得分.

输入输出样例

输入样例#1: 
4
4 5 9 4
输出样例#1: 
43
54
解题思路:
一道环形DP,f[i][j]表示i到j这一段合并成一堆的最大值,f[i][j] = max(f[i][j], f[i][k] + f[k+1][j] + sum[i+1][j]) i<=k<j,对于环形的处理是把这个环复制,接到末尾,其中sum[i+1][j]表示a[i] + a[i+1] + .. + a[j].
AC代码:
 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 using namespace std;
 5 int n,f1[210][210],f2[210][210],num[210],maxans,minans = 9999999,he[105];//f1为最大值,f2为最小值 
 6 int max(int a,int b) {
 7     if(a >= b) return a;
 8     else return b;
 9 }
10 int min(int a,int b) {
11     if(a <= b) return a;
12     else return b;
13 }
14 int main()
15 {
16     cin >> n;
17     memset(f2,0x7f7f7f,sizeof(f2));//将最小值初始位足够大 
18     for(int i = 1; i <= n; i++) {//读入 
19         scanf("%d",&num[i]);
20         he[i] = he[i-1] + num[i];
21         f2[i][i] = 0;
22     }
23     for(int i = n+1; i <= n+n; i++) {//将这个环复制一遍接到末尾 
24         num[i] = num[i-n];
25         he[i] = he[i-1] + num[i];
26         f2[i][i] = 0;
27     }    
28     for(int p = 1; p < n; p++)//枚举区间长度 
29         for(int i = 1, j = i + p; i < n + n && j < n + n; i++, j = i + p)//枚举起点和终点 
30             for(int k = i; k < j; k++){//设置断点 
31                 f1[i][j] = max(f1[i][j], f1[i][k] + f1[k+1][j] + he[j] - he[i-1]);//状态转移
32                 f2[i][j] = min(f2[i][j], f2[i][k] + f2[k+1][j] + he[j] - he[i-1]);//状态转移
33             }
34     
35     for(int i = 1; i <= n; i++) {//找最大值和最小值 
36         maxans = max(maxans,f1[i][i+n-1]);
37         minans = min(minans,f2[i][i+n-1]);
38     }            
39     printf("%d
%d",minans,maxans);
40       
41     
42 return 0;
43 }




原文地址:https://www.cnblogs.com/lipeiyi520/p/10358150.html