贪心和分治,这个怕是福州之行我们最熟悉的东西了,今天的老师讲的又很细,表示我都听懂了(超开森),内容如下:
相同点:都是一种思想,并不是一种算法。
分治:顾名思义,分而治之,将一个大的问题分成若干个子问题,然后进行求解,我们希望子问题和原问题属于同一类型,这样方便我们进行计算,但有时后却不尽如人意,所以我们为了达到我们想要的效果,就会将原问题复杂化来达到原问题和子问题同一类型的问题的条件,下面举几个例子:
eg1:八皇后
相信这道题大家都应该很熟悉吧,不知道也没关系,其实就是求在8*8的棋盘上放8个皇后,使这8个皇后互相不能攻击,求总的方案数。先讲讲国际象棋中皇后的攻击规则吧:可以向前,后或斜着走。
原本是一道搜索题,但这题也可以用分治来做,我们先假设放下一个皇后,这样问题就变成了七皇后(虽然有点胡扯),但此时的七皇后是有限制的:在8*8的棋盘上在与已放下的皇后同一行或同一列以及同一条斜线的位置不能放。以此类推,最后将问题解决。(代码不附)
eg2:二分查找
区间查值:分成左右两半,判断所要查找的数在哪一边,并将这个数所在的区间再次进行以上步骤,最终会将区间分成长度为一的序列,如果序列中的数是我们所要查找的数则返回为真,否则为假。
eg3:搜索
搜索其实也用了分治思想,它将一个大的区域分成若干小的部分,不断进行此操作,直至问题能直接解决,然后进行回溯,一层层返回,得到最终的答案。搜索范围变小后还是搜索。
总结以上例子,我们可得出搜索有以下特点:好拆,好合,(最小的)子问题好解,(不超时)子问题不重复。另,还有以下也有分治的思想:快速排序,归并排序,二叉查找树(左子树比节点小,右子树比节点大的树,是平衡树的优化)
数字的分治--快速幂
a^b%p在这样的情况下,b巨大,如果不%p,就得使用高精度,当然,前提是你得会写并且是一个勤奋的孩子,毕竟高精的代码够惊人。但是用分治就可以简洁许多:(b为偶数时)a^b=a^(b/2)*a^(b/2),当a^0时等于1,a^1时等于a,得方程式f(a,b,p)=f(a,b/2,p)^2,当p为奇数时,只需再在后面乘a即可。
代码如下:(伪代码)
int power(int a,int b,int p)
{
if(b==0){return 1%p}//必须模,否则p=1就尴尬了
else if(b==1){return a%p}//当a比b大时,不模后果自负
else
{
longlong res=power(a,b/2,p);//递归调用
res*=res;//平方
res%=p;//取模
if(b%2==1)
{
res*=a;
res%=p;
}
}
}
b=cn*cn-1…c0;
a^b=a^(c0*2^0)*a^(c1*2^1)…a^(cn*2^n);
代码如下:
int power(int a,int b,int p)
{
longlong res=1;
for(long long i=1;x=a;i<=b;i<<=1)//<<=1相当于*2
{
if(i&b){res*=x;res%=p;}//i&b因为2^n只有1个1,其余为零,所以i&b想当然判断当前数是否为零
x*=x; x%=p;
}
return res;
}
序列的分治
逆序对——归并排序。合并两个序列用时O(n),但须额外空间2n。序列长度为1时不用排序。
矩阵快速幂
可以把线性表优化到O(log(n))。
高精度乘法优化
X=A*10^(n/2)+B;
Y=C*10^(n/2)+D;
XY=AC+10^n+(AD+BC)*10^(n/2)+BD;//只做到这里,没有什么用,甚至更慢,时间复杂度为O(n^2*log(n))
AD+BC=(B-A)*(C-D)+BD+AC;
T(n)=3T*(n/2)+On.
贪心
硬币问题
设你有1元,5元,10元,50元,100元的硬币各c1,c2,c3,c4,c5,枚,问当你需要支付A元是最少要用多少枚硬币?
这题很简单,贪心思想也显而易见,只要先用面额大的硬币即可。
合并石子
有n堆石子,要将石子有序地合并起来,至少要选2堆最多要选k堆进行合并,作为新的石子数,合并的代价是新的一堆的石子数,求最大需要多少?最少需要多少?
最小:与合并果子类似(用类似归并排序实现)
最大:将石子降序排序,然后相加,所得结果即为所求。
相同点:都是一种思想,并不是一种算法。
分治:顾名思义,分而治之,将一个大的问题分成若干个子问题,然后进行求解,我们希望子问题和原问题属于同一类型,这样方便我们进行计算,但有时后却不尽如人意,所以我们为了达到我们想要的效果,就会将原问题复杂化来达到原问题和子问题同一类型的问题的条件,下面举几个例子:
eg1:八皇后
相信这道题大家都应该很熟悉吧,不知道也没关系,其实就是求在8*8的棋盘上放8个皇后,使这8个皇后互相不能攻击,求总的方案数。先讲讲国际象棋中皇后的攻击规则吧:可以向前,后或斜着走。
原本是一道搜索题,但这题也可以用分治来做,我们先假设放下一个皇后,这样问题就变成了七皇后(虽然有点胡扯),但此时的七皇后是有限制的:在8*8的棋盘上在与已放下的皇后同一行或同一列以及同一条斜线的位置不能放。以此类推,最后将问题解决。(代码不附)
eg2:二分查找
区间查值:分成左右两半,判断所要查找的数在哪一边,并将这个数所在的区间再次进行以上步骤,最终会将区间分成长度为一的序列,如果序列中的数是我们所要查找的数则返回为真,否则为假。
eg3:搜索
搜索其实也用了分治思想,它将一个大的区域分成若干小的部分,不断进行此操作,直至问题能直接解决,然后进行回溯,一层层返回,得到最终的答案。搜索范围变小后还是搜索。
总结以上例子,我们可得出搜索有以下特点:好拆,好合,(最小的)子问题好解,(不超时)子问题不重复。另,还有以下也有分治的思想:快速排序,归并排序,二叉查找树(左子树比节点小,右子树比节点大的树,是平衡树的优化)
数字的分治--快速幂
a^b%p在这样的情况下,b巨大,如果不%p,就得使用高精度,当然,前提是你得会写并且是一个勤奋的孩子,毕竟高精的代码够惊人。但是用分治就可以简洁许多:(b为偶数时)a^b=a^(b/2)*a^(b/2),当a^0时等于1,a^1时等于a,得方程式f(a,b,p)=f(a,b/2,p)^2,当p为奇数时,只需再在后面乘a即可。
代码如下:(伪代码)
int power(int a,int b,int p)
{
if(b==0){return 1%p}//必须模,否则p=1就尴尬了
else if(b==1){return a%p}//当a比b大时,不模后果自负
else
{
longlong res=power(a,b/2,p);//递归调用
res*=res;//平方
res%=p;//取模
if(b%2==1)
{
res*=a;
res%=p;
}
}
}
b=cn*cn-1…c0;
a^b=a^(c0*2^0)*a^(c1*2^1)…a^(cn*2^n);
代码如下:
int power(int a,int b,int p)
{
longlong res=1;
for(long long i=1;x=a;i<=b;i<<=1)//<<=1相当于*2
{
if(i&b){res*=x;res%=p;}//i&b因为2^n只有1个1,其余为零,所以i&b想当然判断当前数是否为零
x*=x; x%=p;
}
return res;
}
序列的分治
逆序对——归并排序。合并两个序列用时O(n),但须额外空间2n。序列长度为1时不用排序。
矩阵快速幂
可以把线性表优化到O(log(n))。
高精度乘法优化
X=A*10^(n/2)+B;
Y=C*10^(n/2)+D;
XY=AC+10^n+(AD+BC)*10^(n/2)+BD;//只做到这里,没有什么用,甚至更慢,时间复杂度为O(n^2*log(n))
AD+BC=(B-A)*(C-D)+BD+AC;
T(n)=3T*(n/2)+On.
贪心
硬币问题
设你有1元,5元,10元,50元,100元的硬币各c1,c2,c3,c4,c5,枚,问当你需要支付A元是最少要用多少枚硬币?
这题很简单,贪心思想也显而易见,只要先用面额大的硬币即可。
合并石子
有n堆石子,要将石子有序地合并起来,至少要选2堆最多要选k堆进行合并,作为新的石子数,合并的代价是新的一堆的石子数,求最大需要多少?最少需要多少?
最小:与合并果子类似(用类似归并排序实现)
最大:将石子降序排序,然后相加,所得结果即为所求。