分治
分治算法
分治算法即为把一个规模为n的问题,分为几个规模为k的子问题,并且最好使子问题规模大小相近且较小,这些子问题相互独立且与原问题性质相同,这可以使解决问题更具条理性。
分解,将要解决的问题划分成若干规模较小的同类问题;(可以递归的分解)
求解,当子问题划分得足够小时,用较简单的方法解决;
合并,按原问题的要求,将子问题的解逐层合并构成原问题的解。(也可以使用递归……)
经典例题
寻找伪币
题目描述
给你一个装有16枚硬币的袋子。16枚硬币中有一个是伪造的,并且那个伪造的硬币比真的硬币要轻一些。你的任务是找出这枚伪造的硬币。为了帮助你完成这一任务,将提供一台可用来比较两组硬币重量的仪器,比如天平。利用这台仪器,可以知道两组硬币的重量是否相同。(注意:必须用分治做!)
输入格式
一行,包含16个正整数,用空格隔开,每个正整数不大于10, 其中15个数相同,1个数小于其它15个数
输出格式
一行,两个数,用空格隔开 第一个数是伪币的输入编号,第二个数是伪币的重量
样例
样例输入
2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2
样例输出
4 1
代码
与二分思想相近,首先将十六枚伪币分为八个一组,四个一组,二个一组,通过不断减小问题规模,将其简化值两个数比大小。
当然也可以暴枚,只不过……mjl的愤怒……
#include<bits/stdc++.h>
using namespace std;
const int M_AX=1e5+7;
long long a[M_AX];
int main(){
for(int i=0;i<16;i++)std::cin>>a[i];
int left=-1,right=16,mid;
if(a[0]==a[1])mid=a[0];
else mid=a[3];
while(left!=right){
left++,right--;
if(a[left]==a[right])continue;
if(a[left]==mid)std::cout<<right+1<<" "<<a[right]<<endl;
else std::cout<<left+1<<" "<<a[left]<<endl;
return 0;
}
return 0;
}
寻找最值
题目描述
输入一个序列,寻找最大值和最小值并输出(注意:用分治来做)
输入格式
两行 第一行1个数n,表示序列的个数1≤n≤1000000 第二行,n个int范围的数,用空格隔开
输出格式
一行,两个数 最大值和最小值,用空格隔开
样例
样例输入
5
6 8 4 1 9
样例输出
9 1
代码
和上一道题一样,减半再减半,于是乎……就A了,如果用选择结构,也可以A,数据太垃圾,1000ms超时的才是大佬。
为防止被制裁,我选择隐瞒一下我非分治的做法……
#include<bits/stdc++.h>
using namespace std;
const int M_AX=300005;
int n,a[M_AX];
int main(){
std::cin>>n;
for(int i=1;i<=n;i++)std::cin>>a[i];
int _min=1e9,_max=-1e9;
for(int i=1;i<=n;i++){
if(a[i]>_max)_max=a[i];
if(a[i]<_min)_min=a[i];
}
std::cout<<_max<<" "<<_min;
return 0;
}
快速幂
样例
样例输入
2 10
样例输出
思路很简单,别忘记%12345:
当b%2==0 a的b/2的2次方
否则 为a的b/2的2次方乘以a
#include<bits/stdc++.h>
using namespace std;
long long a,b,c=12345,d;
long long Remainder_operation(int b){
if(b==0)return 1;
long long sum=Remainder_operation(b/2)%c;
sum=(sum*sum)%c;
if(b%2==1)sum=(sum*(a%c))%c;
return sum;
}
int main(){
scanf("%lld %lld",&a,&b);
a%=c;
printf("%lld",Remainder_operation(b));
return 0;
}