经典的动态规划问题---------石子合并
1.直线型
问题描述
在一条直线上有n堆石子,每堆有一定的数量,每次可以将两堆相邻的石子合并,合并后放在两堆的中间位置,合并的费用为两堆石子的总数。求把所有石子合并成一堆的最小花费。
输入格式
输入第一行包含一个整数n,表示石子的堆数。
接下来一行,包含n个整数,按顺序给出每堆石子的大小 。
输出格式
输出一个整数,表示合并的最小花费。
样例输入
5
1 2 3 4 5
样例输出
33
典型的区间动规
解题思路
代码
/*
石子合并问题
5
1 2 3 4 5
*/
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAX_N 110
const int INF = 1 << 30;
static int stone[MAX_N];//表示第n个石子的大小
static int fin[MAX_N][MAX_N];//i j 表示从第i块石子开始到j块石子的最小的花费(这个是两堆合并)
static int dir[MAX_N][MAX_N];//i j 表示把从i到j全部的石子合在一起需要的花费 ,只要i j固定,dir[i][j]就是固定的
int dp(int n)
{
for(int r = 2;r <= n;r ++ ){
int index = 0;
for(int i = 1;i < n;i ++ ){
int j = i + r - 1;
fin[i][j] = INF;
for(int k = i ;k < j;k ++ ){
int t = fin[i][k] +fin[k+1][j] + dir[i][j];
if(fin[i][j] > t){
fin[i][j] = t;
}
}
}
cout << fin[1][n] << endl;
}
return fin[1][n];
}
int main()
{
int n;
cout << "请输入石子的堆数" << endl;
while(cin >> n,n){
memset(stone,0,n);
memset(fin,0,MAX_N*MAX_N);
memset(dir,0,MAX_N);
for(int i = 1;i <= n;i ++ ){
cin >> stone[i];
}
for(int i=1;i<=n;i++)
{
fin[i][i]=0;
dir[i][i]=stone[i];
for(int j=i+1;j<=n;j++)
dir[i][j]=dir[i][j-1]+stone[j];
}
cout << "最小的花费为" << dp(n) << endl;
cout << "请输入石子的堆数" << endl;
}
return 0;
}
2.环形
问题描述
在圆形操场上摆放着一行共n堆的石子。现要将石子有序地合并成一堆。规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆石子数记为该次合并的得分。请编辑计算出将n堆石子合并成一堆的最小得分和将n堆石子合并成一堆的最大得分。
样例输入
5
1 2 3 4 5
样例输出
33
算法分析:
环形对于直线型最大的区别即在于,当j大于n时,也可以合并,这是一个循环放置的
代码
/*
/*
石子合并问题
5
1 2 3 4 5
*/
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAX_N 110
const int INF = 30000;
static int stone[MAX_N];//表示第n个石子的大小
static int fin[MAX_N][MAX_N];//i j 表示从第i块石子开始的j块石子的合并起来的最小花费(这个是两堆合并)
//static int dir[MAX_N][MAX_N];//i j 表示把从i到j全部的石子合在一起需要的花费 ,只要i j固定,dir[i][j]就是固定的
static int index = 0;
int n;
int dir(int ,int);
int dp(int n)
{
for(int r = 2;r <= n;r ++ ){
for(int i = 0;i < n;i ++ ){
int j = (i + r - 1)%(n);
fin[i][r] = INF;
for(int k = 1 ;k <= r - 1;k ++ ){
int index1 = (i + k);
if(i+k>=n) index1 = (i+k)-n;
fin[i][r] = min(fin[i][r],fin[i][k] +fin[index1][r - k] + dir(i,j));
}
}
}
return fin[0][n];
}
int dir(int i,int j)//返回从i到j所有石子的和
{
int ans = 0;
if(j<i)
{
for(int a = i;a<=n;a++)
ans += stone[a];
for(int a = 0;a<=j;a++)
ans += stone[a];
}
else
{
for(int a = i;a<=j;a++)
ans += stone[a];
}
return ans;
}
int main()
{
cout << "请输入石子的堆数" << endl;
while(cin >> n,n){
index = 0;
memset(stone,0,MAX_N);
memset(fin,0,MAX_N*MAX_N);
for(int i = 0;i < n;i ++ ){
cin >> stone[i];
}
for(int i=0;i<n;i++)
{
fin[i][1]=0;
}
cout << "最小的花费为" << dp(n) << endl;
cout << "请输入石子的堆数" << endl;
}
return 0;
}