将问题分解,通过求解局部性的小问题来解决原本的问题,这种技巧称为分治法。
实现分治法需要用到递归。
使用递归的技巧,可以将一个问题拆分成两个或更多较小的局部问题,利用递归函数求每个局部问题的解,然后再将整个结果整合,最终解决问题。
分治法:
- 将问题“分割”成局部问题
- 递归地求解局部问题
- 将局部问题的解“整合”,解决原问题。
穷举搜索
题目
现有长度为n的数列A和整数m。请编写一个程序,判断A中任意几个元素相加是否能得到m。A中每个元素只能使用一次。
数列A以及用作问题的q个mi由外界输入,对每个问题输出yes或no。
思路
可以将所有数列组合都列出来,每个元素都有“选”或者“不选”两个选项。
solve(i,m)就可以分为solve(i+1,m)和solve(i+1,m-A[i])这两个局部问题。(其中m-A[i]表示“使用第i个元素”)。
代码
#include <stdio.h>
int n,A[50];
/*从输入值M中减去所选元素的递归函数*/
int solve(int i,int m){
if(m==0) return 1;
if(i>=n) return 0;
int res= solve(i+1,m) || solve(i+1,m-A[i]);
return res;
}
int main(){
int q,M,i;
scanf("%d",&n);//长度n
for(i=0;i<n;i++) scanf("%d",&A[i]);//数组A
scanf("%d",&q);//整数M的个数
for(i=0;i<q;i++){
scanf("%d",&M);//整数M
if(solve(0,M)) printf("yes\n");
else printf("no\n");
}
return 0;
}
科赫曲线
题目
编写一个程序,输入整数n,输出科赫曲线的顶点坐标,该科赫曲线由深度为n的递归调用画出。
思路
koch包含三个参数,分别是递归深度d,以及线段的两个端点p1、p2。这个递归函数首先会求出线段p1p2的三等分点s、t,然后求能够使得线段su、ut、ts组成的正三角的点u。接下来处理一下函数,从而描绘出图形:
- 对线段p1s递归调用koch,输出s的坐标。
- 对线段su递归调用koch,输出u的坐标。
- 对线段ut递归调用koch,输出t的坐标。
- 对线段tp2递归调用koch。
s与t的坐标可通过一下公式计算出来:
s.x=(2p1.x+1p2.x)/3
s.y=(2p1.y+1p2.y)/3
t.x=(1p1.x+2p2.x)/3
t.y=(1p1.y+2p2.y)/3
以点s为起点,将点t逆时针旋转60度,可得到u。
u.x=(t.x-s.x)*cos60°-(t.y-s.y)*sin60°+s.x
u.y=(t.x-s.x)*sin60°-(t.y-s.y)*cos60°+s.y
代码
#include <iostream>
#include <stdio.h>
#include <math.h>
using namespace std;
struct Point{
double x;
double y;
};
void koch(int n,Point a,Point b){
if(n==0) return;
Point s,t,u;
double th=M_PI*60.0/180.0;//将单位从度变为弧度
s.x=(2.0*a.x+1.0*b.x)/3.0;
s.y=(2.0*a.y+1.0*b.y)/3.0;
t.x=(1.0*a.x+2.0*b.x)/3.0;
t.y=(1.0*a.y+2.0*b.y)/3.0;
u.x=(t.x-s.x)*cos(th)-(t.y-s.y)*sin(th)+s.x;
u.y=(t.x-s.x)*sin(th)+(t.y-s.y)*cos(th)+s.y;
koch(n-1,a,s);
printf("%.8f %8.f\n",s.x,s.y);
koch(n-1,s,u);
printf("%.8f %8.f\n",u.x,u.y);
koch(n-1,u,t);
printf("%.8f %8.f\n",t.x,t.y);
koch(n-1,t,b);
}
int main(){
Point a,b;
int n;
scanf("%d",&n);
a.x=0;
a.y=0;
b.x=100;
b.y=0;
printf("%.8f %.8f\n",a.x,a.y);
koch(n,a,b);
printf("%.8f %.8f\n",b.x,b.y);
return 0;
}