第三版《算法导论》中的15.3-6关于动态规划新增题解

题目:

15.3-6假定你希望兑换外汇,你意识到与其直接兑换,不如进行多种外币的一系列兑换,最后兑换到你想要的那种外币,可能会获得更大收益。假定你可以交易n种不同的货币,编号为1,2.....n,兑换从1号货币开始,最终兑换为n号货币。对每两种货币i和j给定汇率rij,意味着你如果有d个单位的货币i,可以兑换dr0个单位的货币j.进行一系列的交易需要支付一定的佣金,金额取决于交易次数。令ck表示k次交易需要支付的佣金。证明:如果对所有k=1,2...n,ck=0,那么寻找最优兑换序列的问题具有最优子结构性质。然后请证明:如果佣金ck为任意值,那么问题不一定具有最优子结构性质。

题目虽然没有要求写出算法,但我也给出算法。

//15.3-6多次兑换货币换取最大收益
#include <iostream>
using namespace std;
#define n 4
struct Currency_Exchange
{
	double **r;//储存汇率
	int *s;//储存外汇最佳方式的变量
	Currency_Exchange()
	{
		r=new double *[n];
		for (int i=0;i<n;i++)
		{
			r[i]=new double[n];
		}
		s=new int [n];
	}
};
struct Best_Currency_Exchange
{
	static double Max;//最大收益
	static double Min;//最小收益
	int *t;
	static int end;
	Best_Currency_Exchange()
	{
        t=new int[n];
	}
};
double Best_Currency_Exchange::Max=-0x7fffffff;
double Best_Currency_Exchange::Min=0x7fffffff;
int Best_Currency_Exchange::end=0x7fffffff;
void init(Currency_Exchange T,Best_Currency_Exchange Result)//结构体的初始化
{
	for (int i=0;i<n;i++)
	{
		for (int j=0;j<n;j++)
		{
			T.r[i][j]=-0x7fffffff;
		}
		T.s[i]=-0x7fffffff;
		Result.t[i]=-0x7fffffff;
	}
	Result.end=-1;
}
inline void swap(int* array, unsigned int i, unsigned int j)
{
    int t = array[i];
    array[i] = array[j];
    array[j] = t;
}
void Print(int i)
{
	switch (i)
	{
	case 0:cout<<"欧元(EUR-1)-->";
		break;
	case 1:cout<<"坡元(SGD-2)-->";
		break;
	case 2:cout<<"加元(CAD-4)-->";
		break;
    case 3:cout<<"澳元(AUD-3)-->";
		break;
	case 4:cout<<"日元(JPY)-->";
		break;
	case 5:cout<<"台币(CHF)-->";
		break;
	case 6:cout<<"英镑(GBP)-->";
		break;
	default: cout<<"超出范围,输入错误!"<<endl;
		break;
	}
}
Best_Currency_Exchange  FullArray(int* array, size_t array_size, unsigned int index,Currency_Exchange T,Best_Currency_Exchange Result)
{
	static int k=0;
	if(index>=0)
    {
		k++;
		int j=0;
		cout<<"k="<<k<<":";
		double m=1.0;
		if (index>=1)
		{
			m*=T.r[0][array[0]];
		    T.s[0]=0;
			for(unsigned int i = 0; i < index; ++i)
			{
			//	cout <<array[i] << ' ';
				Print(array[i]);cout<<' ';
				T.s[i+1]=array[i];
				if (i==index-1)
				{
					m*=T.r[array[i]][n-1];
				}
				else m*=T.r[array[i]][array[i+1]];
			}
			if(i<n-1)
			{
               T.s[i+1]=n-1;
			}
			i++;
			j=i;
		}
		else if(index==0)
		{
			m=T.r[0][n-1];
			T.s[j++]=0;
			T.s[j++]=n-1;
		}
		cout<<"m="<<m;
		if (Result.Max<m)
		{
			Result.Max=m;
			Result.end=j;
			for (int q=0;q<=Result.end;q++)
			{
				Result.t[q]=T.s[q];
			}
		}
		if (Result.Min>m)
		{
			Result.Min=m;
		}
        cout << '\n';
    }
    if (index==array_size)
    {
		return  Result;
    }
    for(unsigned int i = index; i < array_size; ++i)
    {
        swap(array, i, index);  

        FullArray(array, array_size, index + 1,T,Result);  

        swap(array, i, index);
    }
	return  Result;
}
void main()
{
    Currency_Exchange T;
	Best_Currency_Exchange Result;
	init(T,Result);//结构体的初始化
	for (int i=0;i<n;i++)
	{
		for (int j=0;j<n;j++)
		{
			cout<<"请输入";
			Print(i);
			cout<<"-->";
			Print(j);
			cin>>T.r[i][j];
			cout<<endl;
		}
	}
	int array[n-2]={1,2};
	Best_Currency_Exchange Result1=FullArray(array, n-2,0, T,Result);
	cout<<"最佳兑换方式:";
	for (int k=0;k<=Result1.end;k++)
	{
		if (Result1.t[k]>-0x7fffffff)
		{
			Print(Result1.t[k]);
		}
	}
	cout<<endl;
	cout<<"最大收益为:";
    cout<<Result1.Max<<endl;
	cout<<"最小收益为:";
	cout<<Result1.Min<<endl;
	cout<<"两者差值为:"<<endl;
	cout<<Result1.Max-Result1.Min<<endl;
}

数据实例图:

不加额外手续费的结果图:

如果算上额外任意手续费就会如下图:

总结:

故手续费为0时,可以用最优子结构,手续费任意时就不能用了。



第三版《算法导论》中的15.3-6关于动态规划新增题解

时间: 2024-11-05 04:51:34

第三版《算法导论》中的15.3-6关于动态规划新增题解的相关文章

算法导论之八(10.1-5单数组实现双端队列)

算法导论第三版P131 题目: 10.1-5 栈插入和删除元素只能在同一端进行,队列的插入操作和删除操作分别在两端进行,与它们不同的,有一种双端队列(deque),其插入和删除操作都可以在两端进行.写出4个时间均为O(1)的过程,分别实现在双端队列插入和删除元素的操作,该队列使用一个数组实现的. 注意点: 1.左右端点指向的位置是类似于队列中的tail端点,是下一个插入操作的位置. 2.然后注意遍历的时候,左端点和右端点的位置关系,有两种可能,所以遍历的方式不一样. 代码: /* * 使用单数组

算法导论中对二叉树链表中 Delete 函数的实现

上一篇博客中对 Delete 函数的实现是根据被删除节点子节点的子节点个数, 分为无子节点, 一个子节点和两个子节点的情况分别考虑的. 而这次的代码是根据算法导论的实现用 C++ 直译过来的, 代码如下: void BinarySearchTree::Delete (const int32_t& value) { auto node = Search (value); if (node == nullptr) { cerr << "There is no such value

算法导论_第十六章_动态规划_creatshare分享会

动态规划 注:该篇为本人原创,转载请注明出处:http://blog.csdn.net/chudongfang2015/article/details/51590817--开心 -.- 个人对动态规划的理解: 1.动态规划是一个付出额外空间来节省时间,就是所谓的空间换时间. 2.动态规划储存每个状态的最优解. 3.动态规划是用来把子问题的结果储存下来,再次用到的时候就不必再进行重复计算. 算法导论对动态规划的解释: 动态规划和分治方法相似,都是通过组合子问题的解来求解原问题,分治方法将问题划分为

JS高级程序设计第三版——在HTML中使用JavaScript

使用<script>元素的方式 外部引用式.行内式.嵌入式. JavaScript引用放在<body>后面的原因 假如在文档的<head>元素中包含所有JavaScript文件,意味着必须等到全部JavaScript代码都被下载.解析和执行完成以后,才能开始呈现页面的内容.对于那些需要很多JavaScript代码的页面来说,这无疑会导致浏览器在呈现页面时出现明显的延迟,而延迟期间的浏览器窗口中将是一片空白. JavaScript在XHTML中的用法 XHTML是一种可扩

算法导论中如何求两个字符串的最长公共子序列

#include<iostream> #include<string> using namespace std; const int max_length=10; void print(int b[max_length][max_length],string X,int i,int j); void LCS(string X,string Y,int b[max_length][max_length],int m,int n ) { int **c=new int*[m+1]; f

[算法导论]练习4.1-5最大连续子数组问题

题目:在线性时间内非递归的求数组的最大连续子数组(连续和最大的子数组). 思路:设最大子数组的和为max,起点和终点位置为s.e,正在扫描的子数组的和为add,起点和终点位置为i.j.max的初始值为-∞. 1.若数组的值全为负,则返回最大值. 2.逐个扫描数组元素,更新add.i.j的值. a.若add的值为正,则和max的值比较,如果大于max的值,更新max.s.e的值为add.i.j. b.若add的值为负,则从i到j这一段的数字只能减少子数组的和,要丢弃,add值清零并重下一个元素重新

算法导论第七章快速排序

一.快速排序概述 关于快速排序,我之前写过两篇文章,一篇是写VC库中的快排函数,另一篇是写了快排的三种实现方法.现在再一次看算法导论,发现对快速排序又有了些新的认识,总结如下: (1).快速排序最坏情况下的时间复杂度为O(n^2),虽然最坏情况下性能较差,但快排在实际应用中是最佳选择.原因在于:其平均性能较好,为O(nlgn),且O(nlgn)记号中的常数因子较小,而且是稳定排序. (2).快速排序的思想和合并排序一样,即分治.快排排序的分治思想体现在: a.首先从待排序的数中选择一个作为基数,

《算法导论》第六章 练习题 Exercise

6.1-1 在高度为 h 的堆中,元素最多有 2h+1 - 1 个,最少有 2h  个.注意算法导论里的高度是指深度,从 0 开始而不是从 1 开始. 6.1-2 这很好想,但是不好证明. 由已知高度为 h 的堆,它的元素个数满足 2h   <= n <= 2h+1 - 1 ,解出 lg(n+1) - 1 <= h <= lgn ,但是它不够"合理",因为当 n = 2h+1-1 时,n 等于 2的幂 - 1,此时 lg(n+1) -1 = ?lgn? ,所以 

0-1背包的动态规划算法,部分背包的贪心算法和DP算法------算法导论

一.问题描述 0-1背包问题,部分背包问题.分别实现0-1背包的DP算法,部分背包的贪心算法和DP算法. 二.算法原理 (1)0-1背包的DP算法 0-1背包问题:有n件物品和一个容量为W的背包.第i件物品的重量是w[i],价值是v[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大.其中每种物品只有一件,可以选择放或者不放. 最优子结构性质:对于0-1问题,考虑重量至多W的最值钱的一包东西.如果去掉其中一个物品j,余下的必是除j以外的n-1件物品中,可以带走的重量