[NOI1995] 石子合并
题目描述
在一个圆形操场的四周摆放 \(N\) 堆石子,现要将石子有次序地合并成一堆,规定每次只能选相邻的 \(2\) 堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出一个算法,计算出将 \(N\) 堆石子合并成 \(1\) 堆的最小得分和最大得分。
输入格式
数据的第 \(1\) 行是正整数 \(N\),表示有 \(N\) 堆石子。
第 \(2\) 行有 \(N\) 个整数,第 \(i\) 个整数 \(a_i\) 表示第 \(i\) 堆石子的个数。
输出格式
输出共 \(2\) 行,第 \(1\) 行为最小得分,第 \(2\) 行为最大得分。
样例 #1
样例输入 #1
4
4 5 9 4
样例输出 #1
43
54
提示
\(1\leq N\leq 100\),\(0\leq a_i\leq 20\)。
AC code:
#include <bits/stdc++.h>
#define print(x) cout << #x << '=' << x << '\n'
using namespace std;
using i64 = long long;
i64 max(i64 a,i64 b)
{
return a>b?a:b;
}
i64 min(i64 a,i64 b)
{
return a<b?a:b;
}
void find(vector<i64> &arr, int n)
{
vector<i64> sum(2 * n + 10, 0);
vector<vector<i64>> dp(2 * n + 10, vector<i64>(2 * n + 1, 0x7fffffff));
vector<vector<i64>> dp2(2 * n + 10, vector<i64>(2 * n + 1, -0x7fffffff));
for (int i = 1; i < 2 * n; i++)
{
dp2[i][i] =0; dp[i][i] = 0;
sum[i] = sum[i - 1] + arr[i];
}
for (int len = 2; len <= n; len++)
{
for (int i = 1; i+len-1 <= 2*n; i++)
{
// 区间起点
int j = i + len - 1;
// 终点
for (int k = i; k < j; k++)
{
dp2[i][j] = max(dp2[i][j], dp2[i][k] + dp2[k + 1][j] + sum[j] - sum[i - 1]);
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + sum[j] - sum[i - 1]);
}
}
}
i64 _min = 0x7fffffff;
i64 _max=-1;
for (int i = 1; i <= n; i++)
{
_max = max(dp2[i][i + n - 1],_max);
_min = min(dp[i][i + n - 1], _min);
}
std::cout << _min << '\n';
std::cout << _max <<'\n';
}
int main()
{
int n;
std::cin >> n;
vector<i64> arr(2 * n + 10,0);
for (int i = 1; i <= n; i++)
{
std::cin >> arr[i];
arr[i + n] = arr[i];
}
find(arr, n); // 查找最大值不能用贪心,这也得用区间dp
return 0;
}
标签:10,arr,入门,int,石子,vector,区间,dp
From: https://www.cnblogs.com/cxjy0322/p/18315154