Luogu P1880 石子合并

 P1880 石子合并

题目描述

在一个圆形操场的四周摆放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

 

  石子合并问题是经典问题。主要分为3类:1、任意两堆石子合并。2、链状相邻两堆石子合并。3、环状的相邻两堆石子合并。

  假设石子合并问题要求合并所得最小值(最大值与最小值一个道理,这里只讲最小值)。

  第一类: 每次找出最小的两堆石子进行合并。可以采用优先队列实现。

  第二类:dp。 dp[i][j] 是从i到j合并石子,所需要最小值。

  转移方程是 dp[i][j] = min(dp[i][j], dp[i][i+k]+dp[i+k+1][j])+sum[i][j]。

  sum[i][j]是表示i到j的石子和。 dp[i][i+k]+dp[i+k+1][j]的意思是不断的分成两堆,看哪两堆是最小值。

  边界条件是dp[i][i] = 0 因为这堆和本身合并不得分。

  第三类:dp。 其实是第二类的变形,只需要 断环为链 就可以。

  详细看代码吧。

 1 #include <cstdio>
 2 
 3 #define ll long long
 4 #define INF 1<<30
 5 //开long long,不然f会爆。。。
 6 ll aa[210],f1[210][210],f2[210][210],w[210], ans1, ans2;    //f1->min  f2->max
 7 //f1是求最小得分 f2是求最大得分
 8 ll Min(ll a, ll b)
 9 {
10     return (a < b ? a : b);
11 }
12 ll Max(ll a, ll b)
13 {
14     return (a > b ? a : b);
15 }
16 
17 int main()
18 {
19     int n;
20     scanf("%d", &n);
21     for(int i=1; i<=n; i++)
22     {
23         scanf("%lld", &aa[i]);
24         aa[i+n] = aa[i];//断环为链
25     }
26     for(int i=1; i<2*n; i++)
27         w[i] = w[i-1] + aa[i];//处理前缀和
28     //v表示当前合并i到第i+v个石子 最多合并i到 i+n-1个石子
29     for(int v=1; v<n; v++)
30         for(int i=1; i<2*n-v; i++)
31         {
32             f1[i][i+v] = INF;
33             f2[i][i+v] = 0;
34             for(int k=0; k<v; k++)
35             {    //这里一定要检查是否拼错 我就因为把f2写成了f1而导致WA 查错好长时间才找出来
36                 //注意f1与min对应 f2与max对应。
37                 f1[i][i+v] = Min(f1[i][i+v], f1[i][i+k]+f1[i+k+1][i+v]);
38                 f2[i][i+v] = Max(f2[i][i+v], f2[i][i+k]+f2[i+k+1][i+v]);
39             }//不要忘记加他们的sum
40             f1[i][i+v] += (w[i+v] - w[i-1]);
41             f2[i][i+v] += (w[i+v] - w[i-1]);
42         }
43     
44     
45     ans1 = INF; ans2 = 0;
46     //寻找结果
47     for(int i=1; i<=n; i++)
48     {
49         ans1 = Min(ans1, f1[i][i+n-1]);
50         ans2 = Max(ans2, f2[i][i+n-1]);
51     }
52     
53     printf("%lld\n%lld", ans1, ans2);
54     return 0;
55 }

 

posted @ 2017-10-30 13:30  yBaka  阅读(245)  评论(0编辑  收藏  举报