3-3 石子合并问题
问题描述:在一个圆形操场的四周摆放着 n 堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的 2 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。试设计一个算法,计算出将 n 堆石子合并成一堆的最小得分和最大得分。
算法设计:对于给定 n 堆石子,计算合并成一堆的最小得分和最大得分。
数据输入:由文件 input.txt 提供输入数据。文件的第 1 行是正整数 n(1≤n≤100),表示有 n 堆石子。第 2 行有 n 个数,分别表示每堆石子的个数。
结果输出:将计算结果输出到文件 output.txt。文件第 1 行的数是最小得分,第 2 行中的数是最大得分。
代码如下:
#include <iostream>
#include <fstream>
#include <climits>
#include <cstring>
using namespace std;
const int MAX_LEN=199; // 因为最大N是100,复制后最大长度是2*N-1=199
int stones[MAX_LEN+1]; // 存放石子的数组
int prefixSum[MAX_LEN+1]; // 前缀和数组
int dpMin[MAX_LEN+1][MAX_LEN+1]; // 用于记录最小合并得分的dp数组
int dpMax[MAX_LEN+1][MAX_LEN+1]; // 用于记录最大合并得分的dp数组
int main() {
ifstream inFile("input.txt"); // 记得在当前目录创建input.txt并且将数据保存进去
ofstream outFile("output.txt");
// 初始化所有数组为0
memset(stones, 0, sizeof(stones)); // 初始化石子数组为0
memset(prefixSum, 0, sizeof(prefixSum)); // 初始化前缀和数组为0
// 使用循环初始化dpMin为INT_MAX,dpMax为INT_MIN
for(int i=0; i<=MAX_LEN; i++){
for(int j=0; j<=MAX_LEN; j++){
dpMin[i][j]=INT_MAX; // 初始化最小dp数组为INT_MAX
dpMax[i][j]=INT_MIN; // 初始化最大dp数组为INT_MIN
}
}
// 处理输入
int N; // 石子堆数量
inFile>>N;
int len=N*2-1; // 环形处理,只复制N-1个数
// 读取石子的数量并复制环形部分
for(int i=1; i<N; i++){
inFile>>stones[i];
stones[i+N]=stones[i]; // 环形复制到第2N-1位
}
inFile>>stones[N]; // 最后一个石子单独读取
// 计算前缀和,prefixSum[i]表示从第1堆石子到第i堆石子的总和
for(int i=1; i<=len; i++){
prefixSum[i]=prefixSum[i-1]+stones[i]; // 计算前缀和
dpMin[i][i]=0;
dpMax[i][i]=0; // 将两个dp数组的dp[i][i]赋值0
}
// 动态规划求最小和最大合并得分
for(int length=2; length<=N; length++){ // length表示合并的区间长度
for(int i=1; i+length-1<=len; i++){ // i表示区间起点
int j=i+length-1; // j表示区间终点
for(int k=i; k<j; k++){ // k表示合并位置的分界点
// 最小合并得分
dpMin[i][j]=min(dpMin[i][j], dpMin[i][k]+dpMin[k+1][j]+(prefixSum[j]-prefixSum[i-1]));
// 最大合并得分
dpMax[i][j]=max(dpMax[i][j], dpMax[i][k]+dpMax[k+1][j]+(prefixSum[j]-prefixSum[i-1]));
}
}
}
// 找出最小和最大得分,遍历所有的合并起点
int minScore=INT_MAX;
int maxScore=INT_MIN;
for(int i=1; i<=N; i++){
minScore=min(minScore, dpMin[i][i+N-1]);
maxScore=max(maxScore, dpMax[i][i+N-1]);
}
// 输出最小得分和最大得分
outFile<<minScore<<endl;
outFile<<maxScore<<endl;
inFile.close();
outFile.close();
return 0;
}
// 运行完成后打开output.txt文件即可看到结果
3-6 租用游艇问题
问题描述:长江游艇俱乐部在长江上设置了n个游艇出租站1, 2, ···, n。游客可在这些游艇出租站租用游艇,并在下游的任何一个游艇出租站归还游艇。游艇出租站i到游艇出租站j之间的租金为r(i,j) (1≤i<j≤n)。试设计一个算法,计算出从游艇出租站1到游艇出租站n所需的最少租金。
算法设计:对于给定的游艇出租站i到游艇出租站j之间的租金为r(i,j) (1≤i<j≤n),计算从游艇出租站1到游艇出租站n所需的最少租金。
数据输入:由文件input.txt提供输入数据。文件的第1行中有1个正整数n (n≤200),表示有n个游艇出租站。接下来的n-1行是r(i,j) (1≤i<j≤n)。
结果输出:将算出的从游艇出租站1到游艇出租站n所需的最少租金输出到文件output.txt。
代码如下:
#include <iostream>
#include <vector>
#include <climits>
#include <fstream>
using namespace std;
int main() {
ifstream inFile("input.txt"); // 记得在当前目录创建input.txt并且将数据保存进去
ofstream outFile("output.txt");
int n;
inFile>>n;
// 初始化租金矩阵r(i, j)
vector<vector<int>> rent(n, vector<int>(n, 0));
// 输入租金数据
for(int i=0; i<n-1; i++){
for(int j=i+1; j<n; j++){
inFile>>rent[i][j];
}
}
// 动态规划数组,dp[j] 表示到达游艇站 j 的最小租金
vector<int> dp(n, INT_MAX);
dp[0] = 0; // 到达站1(索引为0)的租金为0
// 动态规划计算
for(int j=1; j<n; j++){
for(int i=0; i<j; i++){
dp[j]=min(dp[j], dp[i]+rent[i][j]);
}
}
outFile<<dp[n-1]<<endl;
inFile.close();
outFile.close();
return 0;
}
// 运行完成后打开output.txt文件即可看到结果
3-8 最小m段和问题
问题描述:给定n个整数组成的序列,现在要求将序列分割为m段,每段子序列中的数在原序列中连续排列。如何分割才能使这m段子序列的和的最大值达到最小?
算法设计:给定n个整数组成的序列,计算该序列的最优m段分割,使m段子序列的和的最大值达到最小。
数据输入:由文件input.txt提供输入数据。文件的第1行中有2个正整数n和m。正整数n是序列的长度;正整数m是分割的段数。接下来的1行中有n个整数。
结果输出:将计算结果输出到文件output.txt。文件的第1行中的数是计算出的m段子序列的和的最大值的最小值。
代码如下:
#include <iostream>
#include <vector>
#include <climits>
#include <fstream>
using namespace std;
int main() {
// 文件输入输出
ifstream inFile("input.txt"); // 记得在当前目录创建input.txt并且将数据保存进去
ofstream outFile("output.txt");
int n, m;
inFile>>n>>m;
vector<int> arr(n);
vector<long long> prefix_sum(n+1, 0);
// 读取数组并计算前缀和
for(int i=0; i<n; i++){
inFile>>arr[i];
prefix_sum[i+1]=prefix_sum[i]+arr[i];
}
// 动态规划数组初始化
vector<vector<long long>> dp(n+1, vector<long long>(m+1, LLONG_MAX));
dp[0][0]=0;
// 动态规划求解
for(int i=1; i<=n; i++){
for(int j=1; j<=m; j++){
for(int k=0; k<i; k++){
dp[i][j]=min(dp[i][j], max(dp[k][j-1], prefix_sum[i]-prefix_sum[k])); // 考虑最后一段是从第k+1个元素到第i个元素之间的值
}
}
}
// 输出最终结果
outFile<<dp[n][m]<<endl;
// 关闭文件
inFile.close();
outFile.close();
return 0;
}
// 运行完成后打开output.txt文件即可看到结果