分治算法
什么是分治算法(Divide and Conquer)是一种解决复杂问题的算法设计技巧,它的基本思想是将一个大问题分解为若干个较小的子问题,然后递归地解决这些子问题,最后将子问题的解合并为原问题的解。
【分治的优化】
分治算法的优点是可以简化复杂问题的求解过程,利用递归和分而治之的思想,降低问题的难度和复杂度。分治算法也可以利用并行计算和缓存优化等技术提高效率。
【可以解决的问题】
该问题的规模缩小到一定的程度就可以容易地解决;
该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质;
利用该问题分解出的子问题的解可以合并为该问题的解;
该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。
【算法步骤】
分治算法可以用递归来实现,分治算法的一般步骤是:
- 分解:将原问题分解为若干个规模较小,相互独立 ,与原问题形式相同的子问题;
- 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题;
- 合并:将各个子问题的解合并为原问题的解。
归并排序
归并排序是一种基于分治思想的排序算法,它的基本思路是:
- 分解:将待排序的数组分成两个部分,左半部分和右半部分,如果数组长度大于1,则继续分解;
- 解决:对左半部分和右半部分分别进行归并排序,递归地调用自身;
- 合并:将排好序的左半部分和右半部分合并成一个有序的数组。
例 三友行OJ( www.oj7.c)求逆序对。
【题目描述】有一实数序列A[1]、A[2]、A[3]、……A[n-1]、A[n](n<10000),若i<j,并且A[i]>A[j],则称A[i]、A[j]构成了一个逆序对,求数列A中逆序对的个数。
例如,5 2 4 6 2 3 2 6,可以逆序对有
(5 2),(5 4),(5 2),(5 3) ,(5 2),
(4 2)(4 3)(4 2 ),
(6 2),(6 3),(6 2),
(3 2)
共:12个
【输入】
共两行,第一行有一个正整数n,第二行有n个整数。
【输出】
只有一行为逆序对个数。
【样例输入】
8
5 2 4 6 2 3 2 6
【样例输出】
12
#include<bits/stdc++.h>
using namespace std;
#define MAXN 10001
int n,a[MAXN],r[MAXN];
long long ans=0;
void MergSort(int s,int t){//s是起点,t是终点
if(s==t) return;
int m=(s+t)/2;//也可以使用int m=(s+t)>>1;
MergSort(s,m);
MergSort(m+1,t);
//到这一步就是归并排序完成,左右都有序
int i=s;
int j=m+1;
int k=s;
while(i<=m && j<=t){
if(a[i]<=a[j])
r[k++]=a[i++];
else{
ans+=m-i+1;
r[k++]=a[j++];
}
}
while (i<=m) r[k++]=a[i++];
while (j<=t) r[k++]=a[j++];
for(i=s;i<=t;i++) a[i]=r[i];
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
MergSort(1,n);
cout<<ans;
return 0;
}
归并排序算法是一个典型的分治算法,它将待排序的数组分成两个部分,对每个部分进行归并排序,然后将两个有序的部分合并成一个有序的数组。归并排序算法的递推式为
一元三次方程求解
有形如
a
x
3
+
b
x
2
+
c
x
+
d
=
0
ax^3+bx^2+cx+d=0
ax3+bx2+cx+d=0这样的一个一元三次方程。给出该方程中各项的系数(a,b,c,d均为实数),并约定该方程存在三个不同实根(根的范围在-100至100之间),且根与根之差的绝对值>=1。
要求由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点后2位。
提示:记方程f(x)=0,若存在2个数x1和x2,且x1<x2,,f(x1)*f(x2)<0,则在(x1,x2)之间一定有一个根。
【输入】
a,b,c,d
【输出】
三个实根(根与根之间留有空格)
【输入样例】
【输入】
1 -5 -4 20
【输出】
-2.00 2.00 5.00
【思路分析】
思路1 :枚举法
根据根的值域和根与根之间的间距要求(>=1),我们不妨将根的值域扩大100倍(-10000<=x<=10000),依次枚举该区间的每一个整数值x,并在题目要求的精度内设定区间x1=,x2=。若区间端点的函数值f(x1)和f(x2)异号或者在区间端点x1的函数值f(x1)=0,则确定为f(x)=0的一个根。
由此得出算法:
输入方程中各项的系数a,b,c,d;
for(x=-10000; x <= 10000; x++){//枚举当前根*100的可能范围
x1=(x-0.05)/100;x2=(x+0.05)/100;//在题目要求的精度内设定区间
if(f(x1)*f(x2) < 0 || f(x1)==0) //若在区间两端的函数值异号或在x1处的函数值为0,则确定x/100为根
printf("%.2f",x/100);
}
//其中函数f(x)计算$a*x^3+b*x^2+c*x+d$:
double f(double x){
f=a*x*x*x+b*x*x+c*x+d;
}
思路2:分治法
枚举根的值域中的每一个整数x(100<=x<=100)。由于根与根之差的绝对值>=1,因此设定搜索区间[x1,x2],其中x1=x,x2=x+1。若
(1)f(x1)=0,则确定x1为f(x)的根;
(2)f(x1)*f(x2)>0,则确定根x不在区间[x1,x2]内,设定[x2,x2+1]为下一个搜索区间;
(3)f(x1)*f(x2)<0,则确定根x在区间[x1,x2]内。
如果确定根x在区间[x1,x2]内的话 (f(x1)*f(x2)<0),如何在该区间找到根的确切位置。采用二分法,将区间[x1,x2]分成左右两个子区间:左子区间[x1,x]和右子区间[x,x2](其中x=):
如果f(x1)*f(x2)<=0,则确定根在左区间[x1,x]内,将x设定为该区间的右指针(x2=x),继续对左区间进行对分;如果f(x1)*f(x)>0,则确定根在右区间[x,x2]内,将x设为该区间的左指针(x1=x),继续对右区间进行对分;
上述对分过程一直进行到区间的间距满足精度要求为止(x2-x1<0.001)。此时确定x1为f(x)的根。
#include<bits/stdc++.h>
using namespace std;
float a,b,c,d;
int n;
float ans[4];
float f(float x){
return a*x*x*x+b*x*x+c*x+d;
}
void solve(float l,float r){
if(f(l)*f(r)>0 && ((r-1)<1 || n>=2)) return ;
float mid=(l+r)/2;
if(f(mid)<=1e-4 && f(mid)>=1e4 ){//近似为0
ans[++n]=mid;
return;
}
solve(l,mid);
solve(mid,r);
}
int main()
{
cin>>a>>b>>c>>d;
solve(-100,100);
cout<<fixed<<setprecision(2)<<ans[1]<<" "<<ans[2]<<" "<<ans[3]<<endl;
return 0;
}
循环比赛日程表(match)
【问题描述】
设有N个选手进行循环比赛,其中N=
2
M
2^M
2M,要求每名选手要与其他N-1名选手都赛一次,每名选手每天比赛一次,循环赛共进行N-1天,要求每天没有选手轮空。
输入:M
输出:表格形式的比赛安排表
【样例输入】match.in
3
【样例输出】match.out
1 2 3 4 5 6 7 8
2 1 4 3 6 5 8 7
3 4 1 2 7 8 5 6
4 3 2 1 8 7 6 5
5 6 7 8 1 2 3 4
6 5 8 7 2 1 4 3
7 8 5 6 3 4 1 2
8 7 6 5 4 3 2 1
#include<bits/stdc++.h>
using namespace std;
const int Maxn=33,Maxm=5;
int matchlist[Maxn][Maxn];
int m;
int main()
{
cin>>m;
int n=1<<m;//2的m次方
即$2^m$
int k=1,half=1;
matchlist[0][0]=1;
while(k<=m){
for(int i=0;i<half;i++)
for(int j=0;j<half;j++)
{
matchlist[i][j+half]=matchlist[i][j]+half;
}
for(int i=0;i<half;i++)
for(int j=0;j<half;j++)
{
matchlist[i+half][j]=matchlist[i][j+half];
matchlist[i+half][j+half]=matchlist[i][j];
}
half*=2;
k++;
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++)
cout<<matchlist[i][j]<<" "<<;
cout<<endl;
}
return 0;
}