/*
=========程序信息========
对应题目:noip2006提高组_能量项链
使用语言:c++
使用编译器:dev c++
使用算法:动态规划
算法运行时间:O(n^3)
作者:贵州大学05级 刘永辉
昵称:SDJL
编写时间:2008年9月
联系QQ:44561907
E-Mail:44561907@qq.com
获得更多文章请访问我的博客:www.cnblogs.com/sdjl
如果发现BUG或有写得不好的地方请发邮件告诉我:)
转载请保留此信息:)
=========题目解析========
此题的解析使用动态规划的9步思考方法,具体细节请参考《通过金矿模型介绍动态规划》一文
9步思考法步骤如下:
1、构造问题所对应的过程。
2、思考过程的最后一个步骤,看看有哪些选择情况。
3、找到最后一步的子问题,使得子问题符合“最优子结构”。
4、确保符合“子问题重叠”。
5、找到边界。
6、确保满足“子问题独立”。
7、考虑如何做备忘录。
8、分析所需时间是否满足要求。
9、写出转移方程式。
此题解析如下:
1、过程很明显,就是合并珠子的过程,从一串珠子共n颗开始,每次合并相邻的两颗为一颗,直到剩下一颗为止。
2、过程的最后一个步骤是把两颗珠子进行合并,这两颗珠子可以这样考虑,其中一颗是连续的r颗合并得到的,另一颗是其余的n-r颗连续的珠子合并得到的,那么选择
就是这个r颗连续的珠子如何选取,以顺时针选取考虑每颗珠子开头的情况,共有n种选择。
3、子问题是选择的这r颗连续的珠子最多能够得到多少能量,以及另外n-r颗连续的珠子最多能够得到多少能量,如果子问题能够得到最优的值,那么母问题在n种选择中
取出最大值则也必然最优,所以符合“最优子结构”。
4、母问题和子问题都是如何在一段珠子中选择一个左、右隔断点,使得最后在隔断点进行合并从而获得最多的能量,不同的是第一个问题需要考虑的是圆形项链,其它
问题需要的是考虑线形项链,因此我们可以把第一个问题选中r颗珠子后剩余的问题(线形的)和子问题(所有子问题都是线形的)看成是重叠的。
5、边界就是仅有一颗或两颗珠子的时候。
6、每一个问题的子问题有两个,都是考虑如何进行合并,因为子问题之间不会使用到同一颗珠子,所以满足独立。
7、用energy[i][j]记录从第i颗珠子到第j颗珠子组成的珠子片断能够得到的最大能量。
8、子问题数为O(n^2),每个问题有O(n)个选择,时间为O(n^3),满足n<=100。
9、参考程序代码。
*/
#include <cstdlib>
#include <iostream>
#include <fstream>
using namespace std;
const int maxCount = 100;//程序允许的最多珠子数
int n;//珠子数
int v[maxCount * 2];//v[i]表示第(i % n)颗珠子的能量,%表示求余
int energy[maxCount * 2][maxCount * 2];//energy[i][j]表示从第i颗珠子到第j颗珠子组成的珠子片断能够得到的最大能量
//初始化
void init()
{
ifstream inputFile("energy.in");
inputFile>>n;
for(int i=0; i<n ; i++)
{
inputFile>>v[i];
v[i + n] = v[i];//保存两便,便于后面的“剪断”操作
}
inputFile.close();
for(int i=0; i<maxCount; i++)
for(int j=0; j<maxCount; j++)
{
energy[i][j] = -1;//等于-1表示未知
}
}
//获得第i颗珠子到第j颗珠子组成的珠子片断能够得到的最大能量
int GetMaxEnergy(int startIndex, int endIndex)
{
//申明能够得到的最大能量
int retMaxEnergy;
//读备忘录
if(energy[startIndex][endIndex] != -1)
{
retMaxEnergy = energy[startIndex][endIndex];
}
//考虑边界只有一颗珠子时
else if(startIndex == endIndex)
{
retMaxEnergy =energy[startIndex][endIndex];
}
//考虑边界只有两颗珠子时
else if(startIndex + 1 == endIndex)
{
retMaxEnergy = v[startIndex] * v[endIndex] * v[endIndex + 1];
}
//考虑最优子结构
else
{
retMaxEnergy = 0;
//循环选取中断点,中断点在k和k+1之间
for(int k=startIndex ; k < endIndex; k++)
{
//获得左边能够得到的能量
int leftEnergy = GetMaxEnergy(startIndex,k);
//获得右边能够得到的能量
int rightEnergy = GetMaxEnergy(k+1,endIndex);
//获得总共能够得到的能量
int totalEnergy = leftEnergy + rightEnergy + (v[startIndex] * v[k+1] * v[endIndex+1]);
//保存最大值
retMaxEnergy = max(retMaxEnergy, totalEnergy);
}
}
//写备忘录
energy[startIndex][endIndex] = retMaxEnergy;
return retMaxEnergy;
}
void show()
{
//申明最优值best
int best = 0;
//最优值为所有k到k+n-1之间获得的能量的最大值,其中k为0到n-1,就好比把圆形项链一剪刀剪为线形的,而k就是剪断的位置
for(int k = 0; k<n; k++)
{
int getEnergy = GetMaxEnergy(k, k + n - 1);
best = max(best, getEnergy);
}
//输出最优值
cout<<best;
}
int main(int argc, char *argv[])
{
//初始化数据
init();
//显示结果
show();
system("PAUSE");
return EXIT_SUCCESS;
}
=========程序信息========
对应题目:noip2006提高组_能量项链
使用语言:c++
使用编译器:dev c++
使用算法:动态规划
算法运行时间:O(n^3)
作者:贵州大学05级 刘永辉
昵称:SDJL
编写时间:2008年9月
联系QQ:44561907
E-Mail:44561907@qq.com
获得更多文章请访问我的博客:www.cnblogs.com/sdjl
如果发现BUG或有写得不好的地方请发邮件告诉我:)
转载请保留此信息:)
=========题目解析========
此题的解析使用动态规划的9步思考方法,具体细节请参考《通过金矿模型介绍动态规划》一文
9步思考法步骤如下:
1、构造问题所对应的过程。
2、思考过程的最后一个步骤,看看有哪些选择情况。
3、找到最后一步的子问题,使得子问题符合“最优子结构”。
4、确保符合“子问题重叠”。
5、找到边界。
6、确保满足“子问题独立”。
7、考虑如何做备忘录。
8、分析所需时间是否满足要求。
9、写出转移方程式。
此题解析如下:
1、过程很明显,就是合并珠子的过程,从一串珠子共n颗开始,每次合并相邻的两颗为一颗,直到剩下一颗为止。
2、过程的最后一个步骤是把两颗珠子进行合并,这两颗珠子可以这样考虑,其中一颗是连续的r颗合并得到的,另一颗是其余的n-r颗连续的珠子合并得到的,那么选择
就是这个r颗连续的珠子如何选取,以顺时针选取考虑每颗珠子开头的情况,共有n种选择。
3、子问题是选择的这r颗连续的珠子最多能够得到多少能量,以及另外n-r颗连续的珠子最多能够得到多少能量,如果子问题能够得到最优的值,那么母问题在n种选择中
取出最大值则也必然最优,所以符合“最优子结构”。
4、母问题和子问题都是如何在一段珠子中选择一个左、右隔断点,使得最后在隔断点进行合并从而获得最多的能量,不同的是第一个问题需要考虑的是圆形项链,其它
问题需要的是考虑线形项链,因此我们可以把第一个问题选中r颗珠子后剩余的问题(线形的)和子问题(所有子问题都是线形的)看成是重叠的。
5、边界就是仅有一颗或两颗珠子的时候。
6、每一个问题的子问题有两个,都是考虑如何进行合并,因为子问题之间不会使用到同一颗珠子,所以满足独立。
7、用energy[i][j]记录从第i颗珠子到第j颗珠子组成的珠子片断能够得到的最大能量。
8、子问题数为O(n^2),每个问题有O(n)个选择,时间为O(n^3),满足n<=100。
9、参考程序代码。
*/
#include <cstdlib>
#include <iostream>
#include <fstream>
using namespace std;
const int maxCount = 100;//程序允许的最多珠子数
int n;//珠子数
int v[maxCount * 2];//v[i]表示第(i % n)颗珠子的能量,%表示求余
int energy[maxCount * 2][maxCount * 2];//energy[i][j]表示从第i颗珠子到第j颗珠子组成的珠子片断能够得到的最大能量
//初始化
void init()
{
ifstream inputFile("energy.in");
inputFile>>n;
for(int i=0; i<n ; i++)
{
inputFile>>v[i];
v[i + n] = v[i];//保存两便,便于后面的“剪断”操作
}
inputFile.close();
for(int i=0; i<maxCount; i++)
for(int j=0; j<maxCount; j++)
{
energy[i][j] = -1;//等于-1表示未知
}
}
//获得第i颗珠子到第j颗珠子组成的珠子片断能够得到的最大能量
int GetMaxEnergy(int startIndex, int endIndex)
{
//申明能够得到的最大能量
int retMaxEnergy;
//读备忘录
if(energy[startIndex][endIndex] != -1)
{
retMaxEnergy = energy[startIndex][endIndex];
}
//考虑边界只有一颗珠子时
else if(startIndex == endIndex)
{
retMaxEnergy =energy[startIndex][endIndex];
}
//考虑边界只有两颗珠子时
else if(startIndex + 1 == endIndex)
{
retMaxEnergy = v[startIndex] * v[endIndex] * v[endIndex + 1];
}
//考虑最优子结构
else
{
retMaxEnergy = 0;
//循环选取中断点,中断点在k和k+1之间
for(int k=startIndex ; k < endIndex; k++)
{
//获得左边能够得到的能量
int leftEnergy = GetMaxEnergy(startIndex,k);
//获得右边能够得到的能量
int rightEnergy = GetMaxEnergy(k+1,endIndex);
//获得总共能够得到的能量
int totalEnergy = leftEnergy + rightEnergy + (v[startIndex] * v[k+1] * v[endIndex+1]);
//保存最大值
retMaxEnergy = max(retMaxEnergy, totalEnergy);
}
}
//写备忘录
energy[startIndex][endIndex] = retMaxEnergy;
return retMaxEnergy;
}
void show()
{
//申明最优值best
int best = 0;
//最优值为所有k到k+n-1之间获得的能量的最大值,其中k为0到n-1,就好比把圆形项链一剪刀剪为线形的,而k就是剪断的位置
for(int k = 0; k<n; k++)
{
int getEnergy = GetMaxEnergy(k, k + n - 1);
best = max(best, getEnergy);
}
//输出最优值
cout<<best;
}
int main(int argc, char *argv[])
{
//初始化数据
init();
//显示结果
show();
system("PAUSE");
return EXIT_SUCCESS;
}