P1775 石子合并(弱化版)

P1775 石子合并(弱化版)

感觉 dp 太难了,这真的感觉太难学了,但是还要写题记积累啊,唉!

感觉不用讲题意了(那你也别讲题解了)就是石子之间可以合并,合并的代价是这堆石子数,问如何合并全部石子后总代价最小。

考虑用区间 dp,设状态为 \(dp[i][j]\) 为区间 \([i,j]\) 的最小代价,转移时先枚举区间长度,然后枚举区间右端点,一个长度大于等于2的区间可以分为左右小区间,所以再枚举中间点分为左右区间,得到转移方程:

\(dp[j][r]=min(dp[j][r],dp[j][k]+dp[k+1][r]+s[j][r]);\)

最后答案就是 \(dp[1][n]\)

合并代价就是区间和,所以可以用前缀和优化。

#include<bits/stdc++.h>
using namespace std;

int n;                    // 序列长度
int dp[305][305];         // 动态规划表,dp[i][j]表示将区间[i, j]合并为一段的最小代价
int s[305];               // 前缀和数组,s[i]表示前i个元素的和,用于快速计算区间和
int a[305];               // 存储输入的序列

int main(){
	ios::sync_with_stdio(false);	// 加速输入输出,避免C++的流式IO同步
	cin >> n;                      // 读取序列长度
	memset(dp,0x3f,sizeof dp);     // 初始化动态规划表,0x3f3f3f3f是一个很大的数,相当于无穷大
	
	for(int i = 1; i <= n; i++){
		cin >> a[i];               // 读取序列中的元素
		dp[i][i] = 0;              // 初始化dp表,当区间[i, i]只有一个元素时,合并代价为0
		s[i] = s[i-1] + a[i];      // 计算前缀和,用于快速获取任意区间的元素和
	}
	
	// 动态规划部分
	for(int len = 2; len <= n; len++){     // len是当前处理的子区间长度,逐渐从2增加到n
		for(int i = 1; i <= n - len + 1; i++){  // i是子区间的起始位置
			int r = i + len - 1;           // r是子区间的结束位置
			for(int k = i; k < r; k++){    // 枚举分割点k,将[i, r]分成[i, k]和[k+1, r]两部分
				dp[i][r] = min(dp[i][r], dp[i][k] + dp[k+1][r] + s[r] - s[i-1]);  
				// 合并代价为子区间[i, k]和[k+1, r]的合并代价之和,再加上区间[i, r]的总和
			}
		}
	}

	cout << dp[1][n];  // 输出整个序列[1, n]的最小合并代价
	return 0;
}
posted @ 2024-09-10 18:49  sad_lin  阅读(14)  评论(0编辑  收藏  举报