动规之石子合并问题(直线型和环形)

经典的动态规划问题---------石子合并

1.直线型

问题描述
  在一条直线上有n堆石子,每堆有一定的数量,每次可以将两堆相邻的石子合并,合并后放在两堆的中间位置,合并的费用为两堆石子的总数。求把所有石子合并成一堆的最小花费。
输入格式
  输入第一行包含一个整数n,表示石子的堆数。
  接下来一行,包含n个整数,按顺序给出每堆石子的大小 。
输出格式
  输出一个整数,表示合并的最小花费。
样例输入

5
1 2 3 4 5

样例输出

33

典型的区间动规
解题思路
动规之石子合并问题(直线型和环形)_第1张图片
动规之石子合并问题(直线型和环形)_第2张图片

动规之石子合并问题(直线型和环形)_第3张图片
代码

/*
石子合并问题 
5
1 2 3 4 5

*/ 
#include
#include
#include

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
#include
#include

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;
 } 

你可能感兴趣的:(动态规划)