分治算法
分治策略:对于一个规模为n的问题,若该问题比较容易解决(比如规模较小)则直接解决,否则将其分解为k个规模较小的子问题.这些子问题互相独立且与原问题形式相同,递归地解决这些子问题,然后将各子问题的解合并得到原问题解.这种算法设计策略叫做分治法.
分治法解决的问题:
(1),二分搜索
(2),最大整数乘法
(3),Strassen矩阵乘法
(4),棋盘覆盖
(5),合并排序
(6),快速排序
(7),线性时间选择排序
(8),最接近点对问题
(9),循环赛日程表
(10),汉诺塔
例:
最大子数组问题
天数: 1 2 3 4 5 6
价格: 12 15 15 16 13 15
变化: 3 0 1 -3 2
3 0 1 -3 2
low mid high
共3种情况:
1,最大子数组开始下标,结束下标都在[low,mid]范围内
2,最大子数组开始下标,结束下标都在[mid,high]范围内
3,最大数组开始下标在mid前,结束下标在mid+1后.
相对3种情况:
相对于上面1 2两种情况采用递归方法。
分治法在每一层递归上都有三个步骤:
分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;
解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题;
合并:将各个子问题的解合并为原问题的解。
第3种就直接for查找在mid前的开始下标和mid+1后的结束下标,然后把两个计算和相加就好了。
代码块:
//最大子数组问题
class Program
{
//暴力求解
void Violence()
{
int[] priceArray = { 12, 15, 15, 16, 13, 15 };
int[] priceFluctuationArray = new int[priceArray.Length - 1];//价格波动的数组
for (int i = 1; i < priceArray.Length; i++)
{
priceFluctuationArray[i - 1] = priceArray[i] - priceArray[i - 1];
}
int total = priceFluctuationArray[0];//默认第一个数元素是最大数组
int startIndex = 0;
int endIndex = 0;
for (int i = 0; i < priceFluctuationArray.Length; i++)
{
//取得以i为子数组起点的所有子数组
for (int j = i; j < priceFluctuationArray.Length; j++)
{
//由i,j就确定了一个子数组
int totalTemp = 0;
//临时最大子数组的和
for (int index = 0; index < j+1; index++)
{
totalTemp += priceFluctuationArray[index];
}
if (totalTemp>total)
{
total = totalTemp;
startIndex = i;
endIndex = j;
}
}
}
Console.WriteLine("---暴力法:");
Console.WriteLine("买入日期:" + (++startIndex));
Console.WriteLine("卖出日期:" + (endIndex+2));
Console.WriteLine("总盈利:" + total);
}
//存放左右边界值以及sum值的结构体
/*特别注意结构体的使用!!!!!!!!!!!*/
struct SubArray
{
public int startIndex;//左部边界下标
public int endIndex;//右部边界下标
public int total;//左右边界之间的数组元素和
}
//分治法
void DivideAndConquer()
{
int[] priceArray = { 12, 15, 15,16, 13,15};
int[] pf = new int[priceArray.Length - 1];//价格波动的数组
for (int i = 1; i < priceArray.Length; i++)
{
pf[i - 1] = priceArray[i] - priceArray[i - 1];
}
SubArray subArray = GetMaxSubArray(0, pf.Length - 1, pf);
Console.WriteLine();
Console.WriteLine("---分治法:");
Console.WriteLine("买入日期:"+(subArray.startIndex+1));
Console.WriteLine("卖出日期:"+(subArray.endIndex+2));
Console.WriteLine("总盈利:"+subArray.total);
}
//这个方法是用来取得array这个数组从low到high之间的最大子数组
SubArray GetMaxSubArray(int low,int high,int[] array)
{
if (low==high)
{
SubArray subarray;
subarray.startIndex = low;
subarray.endIndex = high;
subarray.total = array[low];
return subarray;
}
int mid = (low + high) / 2;//低区间 [low,mid] 高区间 [mid-1,high]
SubArray subArray1 = GetMaxSubArray(low, mid, array);
SubArray subArray2 = GetMaxSubArray(mid + 1, high, array);
//从[low,mid]找到最大子数组[i,mid]
int total1 = array[mid];
int startIndex = mid;
int totalTemp = 0;
for (int i = mid; i >= low; i--)
{
totalTemp += array[i];
if (totalTemp > total1)
{
total1 = totalTemp;
startIndex = i;
}
}
//从[mid+1,high]找到最大子数组[mid+1,j]
int total2 = array[mid + 1];
int endIndex = mid + 1;
totalTemp = 0;
for (int j = mid+1; j <= high; j++)
{
totalTemp += array[j];
if (totalTemp>total2)
{
total2 = totalTemp;
endIndex = j;
}
}
SubArray subArray3;
subArray3.startIndex = startIndex;
subArray3.endIndex = endIndex;
subArray3.total = total1 + total2;
if (subArray1.total >= subArray2.total && subArray1.total >= subArray3.total)
{
return subArray1;
}
else if (subArray2.total >= subArray1.total && subArray2.total >= subArray3.total)
{
return subArray2;
}
else
{
return subArray3;
}
}
static void Main(string[] args)
{
Program a =new Program();
a.Violence();
a.DivideAndConquer();
Console.ReadLine();
}
}