点击下载noip2006提高组题目_代码_测试数据.rar

 通过金矿模型介绍动态规划

 

/*
=========程序信息========
对应题目: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;
}
posted on 2008-09-01 23:18  刘永辉  阅读(3208)  评论(0编辑  收藏  举报