分治与动态规划笔记
1.分治算法框架:
分治法的一般递归算法设计模式如下:
Divide-and-Conquer(int n) //n为问题规模
{
if (n≤n0)//n0为可解子问题的规模
{ 解子问题;
return(子问题的解);
}
for (i=1 ;i<=k;i++) //分解为较小子问题p1,p2,…pk
yi=Divide-and-Conquer(|Pi|); //递归解决Pi
T =MERGE(y1,y2,...,yk); //合并子问题
return(T);
}
分治法的基本步骤在每一层递归上都有三个步骤:
1)分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;
2)解决:若子问题规模较小而容易被解决则直接解,否则再继续分解为更小的子问题,直到容易解决;
3)合并:将已求解的各个子问题的解,逐步合并为原问题的解
2.动态规划:
动态规划求最优值-是枚举,不是递推。
序列可以不连续,子段连续。
动态规划算法的一般模式:
1.划分阶段:按照问题的时间或空间特征,把问题分为若干个阶段。(在划分阶段时,注意有序或可排序)
2.确定状态和状态变量:将问题发展到各个阶段时所处的各种情况用不同状态表示出来;
3.确定决策并写出状态转移方程:一般是根据相邻两个阶段各状态之间的关系来确定决策;
4.寻找终止条件:给出的状态转移方程是一个递推式,必须有一个递推的终止条件;
5.编写程序。
3.例题
最大字段和问题:
int max_sum3(int n, int a[ ])
{ int sum=0,this_sum=0;
for(int i=1;i<=n;i++)
{
if (this_sum>0)
this_sum+=a[i]; //如果第i阶段得到前i个数字的最大子段和大于0则加上a[i]
else
this_sum=a[i];//若小于0则从a[i]重新开始计算
if (this_sum>sum)
sum=this_sum;//如果如果第i阶段得到前i个数字的最大子段和大于最大字段和,则this_sum赋值给sum,若小于则sum[i]=sum[i-1]
}
return sum;
}
最长公共子序列:
*二维数组f[i,j] 表示 X 的 i 位和 Y 的 j 位之前的最长公共子序列的长度
void LCSLength(char *x, char *y, int m, int n, int c[][MAXLEN])
{
int i, j;
for(i = 0; i <= m; i++)
c[i][0] = 0;// X 的 i 位和 Y 的 0位之前的最长公共子序列的长度初始值为0
for(j = 1; j <= n; j++)
c[0][j] = 0;// X 的 0 位和 Y 的 i位之前的最长公共子序列的长度初始值为0
for(i = 1; i<= m; i++)
for(j = 1; j <= n; j++)
if(x[i-1] == y[j-1]) //依次比较x[i],y[j],如果相等就在前i-1/j-1项的基础上加1
{
c[i][j] = c[i-1][j-1] + 1;
}
else if(c[i-1][j] >= c[i][j-1]) //如果x[i]y[j]不相等再取c[i-1][j]与c[i][j-1]中的最大者赋给c[i][j]
{
c[i][j] = c[i-1][j];
else
c[i][j] = c[i][j-1];
}
}
石子合并问题:
左右分成两堆
左边最小得分+右边最小得分+石子数总和(最后合起来)
机器分配问题:
f(i,j) =Max{f[i-1,k] + a[i,j-k]}//前i-1个公司分配到k件机器的最大效益+第i个公司分配到j-k件机器的效益, 然后求各种情况的最大值
(1<=i<=N,1<=j<=M,0<=k<=j )