环形石子合并问题 - 经典DP问题

本文探讨了环形结构下的石子合并问题,通过双倍长度线性化方法将其转化为线性问题解决。介绍了如何利用动态规划求解最优合并顺序,并提供了具体的C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


 

 ============================================================

 

又是一道超经典的题目。 
对于线性的合并石子问题,dp模型类似于“加括号”那类型的dp题目,设 f(i, j)为 将第i项到第j项合并得到的最优解 
关键是,这题目是环形的。环形结构,经常采用双倍长度线性化手段,也就是说,把环形结构看成是长度为环的两倍的线性结构来处理。 
环的长度是N,所以题目相当于有一排石子1....N+N,然后就可以用线性的石子合并问题的方法做了。 
有个要注意的地方,f(i, j) 总是与 f(N +i, N +j) 相等的,所以可以减少一些不必要的计算。 

 

01 #include <cstdio>
02 #include <algorithm>
03 using namespace std;
04  
05 int _sum[205];
06 int a[205];
07 int N;
08 int f[205][205];
09 int g[205][205];
10  
11 int getSegmentSum(int i, int j) {
12   return _sum[j] - _sum[i] + a[i];
13 }
14  
15  
16 int dp(int i, int j) {
17   int & ans = f[i][j];
18   if (ans != -1) return ans;
19  
20   if (i == j) return ans = 0;
21   if (i + 1 == j) return ans = getSegmentSum(i , j);
22   if (i <= N && j <= N) return ans = dp(N + i, N + j);
23  
24   ans = 2000000000;
25   int s = getSegmentSum(i, j);
26   for (int k = i; k < j; ++k) {
27     ans = min(ans, dp(i, k) + dp(k + 1, j) + s);
28   }
29   return ans;
30  
31 }
32  
33  
34 int dp2(int i, int j) {
35   int & ans = g[i][j];
36   if (ans != -1) return ans;
37  
38   if (i == j) return ans = 0;
39   if (i + 1 == j) return ans = getSegmentSum(i , j);
40   if (i <= N && j <= N) return ans = dp2(N + i, N + j);
41  
42   ans = -2000000000;
43   int s = getSegmentSum(i, j);
44   for (int k = i; k < j; ++k) {
45     ans = max(ans, dp2(i, k) + dp2(k + 1, j) + s);
46   }
47   return ans;
48  
49 }
50  
51  
52 void input() {
53   int i, j;
54   scanf("%d", &N);
55   for (i = 1; i <= N; ++i) {
56     scanf("%d", &(a[i]));
57   }
58   for (i = N + 1; i <= N + N; ++i) a[i] = a[i - N];
59   _sum[1] = a[1];
60   for (i = 2; i <= N + N; ++i) _sum[i] = a[i] + _sum[i - 1];
61  
62   for (i = 0; i < 205; ++i)
63     for (j = 0; j < 205; ++j)
64       f[i][j] = g[i][j] = -1;
65  
66  
67 }
68  
69 int main() {
70   input();
71   int i, j;
72   for (i = N + N; i >= 1; --i)
73     for (j = i; j <= N + N; ++j)
74     { dp(i, j); dp2(i, j); }
75  
76   int ans = 2000000000;
77   for (i = 1; i <= N; ++i) {
78     ans = min(ans, dp(i, i + N - 1));
79   }
80  
81   int ans2 = -2000000000;
82   for (i = 1; i <= N; ++i) {
83     ans2 = max(ans2, dp2(i, i + N - 1));
84   }
85   printf("%d\n%d\n", ans, ans2);
86   return 0;
87 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值