Hello everybody!
- 请奆佬们洁身自好,好好打代码从我做起 -
题目大意:
设有 N堆石子排成一排,其编号为 1,2,3,…,N
每堆石子有一定的质量,可以用一个整数来描述
现在要将这 N堆石子合并成为一堆,每次只能合并相邻的两堆
合并的代价为这两堆石子的质量之和
合并后与这两堆石子相邻的石子将和新堆相邻
合并时由于选择的顺序不同,合并的总代价也不相同
找出一种合理的方法,使总的代价最小,输出最小代价
同时输出合并过程
合并石子其实和 合并果子
很像
只是合并 只能相邻,但其实也方便了 区间dp
从最后一次合并开始思考
最后一次合并前,石子肯定只有两堆 ,
设k为两者 中点 , 也就是两堆中间的位置
第一堆是原来的1到k堆,第二堆是原来的第k+1到第n堆。
显然,
要使得总代价最小,必然是该两堆式子之前的合并代价最小。
然后不断拆分成子问题解决
定义状态f(i,j)
表示 第i堆石子到第j堆石子 的 最小合并代价 。
目标状态即为f(1,n)。
方程——f(i,j)=min(f(i,k)+f(k+1,j)+sum(i,j);
sum是什么呢?是一个 区间和 ,我们利用 前缀和 可以将询问sum的复杂度降至 O(1)
int query(int i, int j) //前缀和,sum[i],表示1~i的和,数学内容,简单,自行思考原理
{
return sum[j] - sum[i - 1];
}
为什么是 区间和 呢?
其实 区间和就是两堆的重量(简单,自行思考)
也就是 合并代价
然后是 初始化
因为是求最小值,所以要开一个大数
memset(f, 27, sizeof(f));
27其实计算机会认为是一个很大的数,接近int边界,并不是我们认为的27
也可以换成其他数
for (int i = 1; i <= n; i++)
{
cin >> a[i];
sum[i] = sum[i - 1] + a[i];// 预处理前缀和
f[i][i] = 0;//自己合并自己不需要代价
}
输入可以同时处理
见代码,sum可以求,因为自己合并自己不需要代价 ,所以f(i,i)始终为0
接下来讲解dp部分
由于我们已经知道起点和区间大小,所以可以求终点
然后枚举中点就完了
for (int k = 2; k <= n; k++)//k,枚举区间大小
{ 这个p是什么呢?
p数组用来记录最优解的中点
Why?
for (int i = 1; i <= n; i++) //i,枚举起点
{
int j = i + k - 1;//求终点
for (int l = i; l < j; l++)//枚举中点,这里可以优化,详见PPT《DP之四边形不等式优化》
{
if (f[i][j] > f[i][l] + f[l + 1][j] + query(i, j)) //求最小值
{
f[i][j] = f[i][l] + f[l + 1][j] + query(i, j);
p[i][j] = l;//有新的最小值,更新中点
}
}
}
}
这个p是什么呢?
p数组用来记录最优解的中点
Why?
由于题目要求过程,我们可以用递归求解
这时p就派上用场了
p(i,j)表示合并i,j时的中点k
由此将l,r分成两份,不断递归
void dg(int l, int r)
{
if (l == r) {//说明只有一个数,直接输出!
cout << a[r];//不要换行!不要空格!
return;
}
cout << '(';//输出左右括号
int mid = p[l][r];//读取中点
dg(l, mid);//递归左半部分
cout << ")(";//输出中间括号
dg(mid + 1, r);//递归右半部分
cout << ')';//输出左右括号
}
好了,这就是关于合并石子的全部内容了
上代码!#include <bits/stdc++.h>
using namespace std;
int n, a[1005], f[1005][1005], p[1005][1005], sum[1005];
int query(int i, int j)
{
return sum[j] - sum[i - 1];
}
void dg(int l, int r)
{
if (l == r) {
cout << a[r];
return;
}
cout << '(';
int mid = p[l][r];
dg(l, mid);
cout << ")(";
dg(mid + 1, r);
cout << ')';
}
int main() {
memset(f, 27, sizeof(f));
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
sum[i] = sum[i - 1] + a[i];
f[i][i] = 0;
}
for (int k = 2; k <= n; k++)
{
for (int i = 1; i <= n; i++)
{
int j = i + k - 1;
for (int l = i; l < j; l++)
{
if (f[i][j] > f[i][l] + f[l + 1][j] + query(i, j))
{
f[i][j] = f[i][l] + f[l + 1][j] + query(i, j);
p[i][j] = l;
}
}
}
}
cout << f[1][n] << endl;
dg(1, n);
return 0;
}
其实可以 优化 ,详见《 四边形不等式 优化》
但那是搞n=5000的大数据的......