分治算法例题
分治算法-残缺棋盘

分治算法-残缺棋盘残缺棋盘是⼀个2^k*2^个⽅格的棋盘,其中恰有1个⽅格残缺。
图中给出,其中残缺部分⽤阴影表⽰。
这样的棋盘称为"三格板",残缺棋盘问题就是⽤这四种三格板覆盖更⼤的残缺棋盘。
再次覆盖中要求:(1)两个三格板不能重复。
(2)三格板不能覆盖残缺棋盘⽅格,但必须覆盖到其他所有的⽅格。
算法思路:利⽤分治算法将棋盘细化,逐步解决,以4*4为例⾸先判断残缺的位置在哪⾥,然后在中间填上对应的三格板,如在上图中间拼上三⾓板(4),变成下⾯这样然后通过中间将其分成了4个2*2的⼩残缺棋盘,从⽽问题得以解决4*4的分析过于简单,现在我们以8*8为例进⾏分析,能更清楚的理解这个例⼦中分治算法的思想⾸先将三格板放置在中间,将其分4个⼩的4*4的残缺棋盘通过红⾊线将其划分成4个4*4的残缺棋盘,现在以左上的残缺棋盘为例然后将左的4*4的⼩棋盘右划分成了4个2*2的⼩棋盘,这样就将问题优化成了2*2的三⾓棋盘的⼩问题,这样很快就能将左上的4*4的残缺棋盘解决了下⾯继续分析右上的4*4的棋盘,根据残缺的⽅格在⼩棋盘中的位置,在中间选择合适的三格板将⼩的残缺棋盘继续划分,将问题分化成更⼩的状态接着的问题和上⾯⼀样分析。
右上⾓的⼩棋盘很快也能解决了,下⾯两块残缺棋盘的分析⽅法和上⾯的⼀样,整个问题就这样⼀步步的分解成⼩问题,然后解决了。
下⾯是源代码#include <iostream>using namespace std;int amount,Board[100][100];void Cover(int tr,int tc,int dr,int dc,int size){int s,t;if(size<2)return ;amount++;t=amount;s=size/2;if(dr<tr+s&&dc<tc+s)//残缺在左上⾓{//覆盖中间位置Board[tr+s-1][tc+s]=t;Board[tr+s][tc+s-1]=t;Board[tr+s][tc+s]=t;Cover(tr,tc,dr,dc,s);//覆盖左上Cover(tr,tc+s,tr+s-1,tc+s,s);//覆盖右上Cover(tr+s,tc,tr+s,tc+s-1,s);//覆盖左下Cover(tr+s,tc+s,tr+s,tc+s,s);//覆盖右下}else if(dr<tr+s&&dc>=tc+s)//残缺在右上⾓{Board[tr+s-1][tc+s-1]=t;Board[tr+s][tc+s-1]=t;Board[tr+s][tc+s]=t;Cover(tr,tc,tr+s-1,tc+s-1,s);Cover(tr,tc+s,dr,dc,s);Cover(tr+s,tc,tr+s,tc+s-1,s);Cover(tr+s,tc+s,tr+s,tc+s,s);}else if(dr>=tr+s&&dc<tc+s)//残缺在左下 {Board[tr+s-1][tc+s-1]=t;Board[tr+s-1][tc+s]=t;Board[tr+s][tc+s]=t;Cover(tr,tc,tr+s-1,tc+s-1,s);Cover(tr,tc+s,tr+s-1,tc+s,s);Cover(tr+s,tc,dr,dc,s);Cover(tr+s,tc+s,tr+s,tc+s,s);}else{Board[tr+s-1][tc+s-1]=t;Board[tr+s-1][tc+s]=t;Board[tr+s][tc+s-1]=t;Cover(tr,tc,tr+s-1,tc+s-1,s);Cover(tr,tc+s,tr+s-1,tc+s,s);Cover(tr+s,tc,tr+s,tc+s-1,s);Cover(tr+s,tc+s,dr,dc,s);}}void Print(int s){for(int i=1;i<=s;i++){for(int j=1;j<=s;j++)printf("%5d ",Board[i][j]);printf("\n");}}int main(){int s=1,k,x,y;printf("输⼊2残缺棋盘的规模:2^k,k="); scanf("%d",&k);for(int i=1;i<=k;i++)s*=2;printf("输⼊棋盘残缺位置(x,y):");scanf("%d%d",&x,&y);Board[x][y]=0;Cover(1,1,x,y,s);Print(s);return 0;}。
dda算法画直线例题

dda算法画直线例题dda算法是一种常用于计算机图形学中的算法,用于在计算机中绘制直线或其他几何形状。
本例题将通过dda算法绘制一条直线,帮助读者了解该算法的基本原理和应用。
一、算法概述dda算法是一种分治算法,它将原始的直线绘制问题分解为更小的子问题,逐个解决这些子问题,最终得到完整的绘制结果。
该算法的核心思想是通过逐点地更新像素位置,逐步逼近目标位置,从而实现直线的绘制。
二、实现步骤1.初始化:设置绘图窗口大小,确定要绘制的直线起点和终点。
2.循环迭代:对于每个像素点,按照dda算法的步骤进行更新。
具体步骤如下:a.计算当前像素点到直线起点的距离(dx),并将其与偏移量(delta)比较。
b.如果dx小于或等于delta,则当前像素点已经在直线上,无需进一步更新。
c.否则,根据dda算法的公式计算新的像素位置(new_x),并将其与当前像素位置进行比较。
d.如果new_x小于或等于当前像素位置,则将当前像素位置更新为new_x;否则,继续迭代下一个像素点。
3.重复步骤2直到绘制完整个窗口。
三、代码实现以下是一个简单的代码实现,使用c语言描述dda算法绘制直线的过程:```c#include<stdio.h>#include<stdlib.h>#include<math.h>#defineWIDTH800//绘图窗口宽度#defineHEIGHT600//绘图窗口高度voiddraw_line(intstart_x,intstart_y,intend_x,intend_y){inti,j,dx,dy,delta,new_x,new_y;for(i=0;i<HEIGHT;i++){for(j=0;j<WIDTH;j++){dx=end_x-start_x;dy=end_y-start_y;delta=sqrt(dx*dx+dy*dy);//计算偏移量if(dx<=delta||j==0){//如果当前像素点到直线起点的距离小于或等于偏移量,或者当前像素位置是第一帧,直接输出当前像素位置printf("%d,%d",j,i);}else{//否则,根据dda算法的公式计算新的像素位置并输出new_x=start_x+(j-WIDTH/2)*dx/delta;new_y=start_y+(i-HEIGHT/2)*dy/delta;printf("%d,%d",new_x,new_y);}}printf("\n");//换行}}intmain(){intstart_x=50,start_y=50;//直线起点坐标intend_x=200,end_y=150;//直线终点坐标draw_line(start_x,start_y,end_x,end_y);//绘制直线并输出结果return0;}```这段代码通过循环迭代的方式,逐点更新像素位置,从而实现直线的绘制。
oj分治法题目

以下是OJ分治法题目:
二分查找算法:给定一个有序数组,请你在数组中查找指定元素。
二分查找扩展:给定一个有序数组和一个目标值,请你在数组中查找目标值,并返回其索引。
如果目标值不存在于数组中,则返回-1。
归并排序算法:给定一个无序数组,请将其按照升序排序并输出。
快速排序算法:给定一个无序数组,请将其按照升序排序并输出。
判断回文数组:给定一个数组,判断该数组是否为回文数组。
如果是回文数组,则返回true;否则返回false。
寻找旋转排序数组中的最小值:给定一个旋转排序数组,请找出其中的最小值及其索引。
寻找数组中第k大的元素:给定一个未排序数组和一个整数k,请找出数组中第k大的元素。
判断子序列:给定两个有序数组,判断第一个数组是否为第二个数组的子序列。
如果是,则返回true;否则返回false。
寻找峰值:给定一个单调递增的整数数组,请找出其中的峰值。
寻找两数之和:给定一个整数数组和一个目标值,请找出数组中和为目标值的两个整数,并返回它们的下标。
以上题目都是OJ分治法的经典题目,难度较高。
需要通过将问题分解为更小的子问题来求解。
需要有一定的数据结构和算法基础才能解答这些题目。
分治法经典案例

分治法经典案例
嘿,大家知道吗,这分治法可真是太厉害啦!就拿排序来说吧,比如一堆杂乱无章的数字,哎呀呀,那简直是一团乱麻!这时候分治法就出马啦。
想象一下,你要整理一个超级乱的房间,你会怎么做?当然是把房间分成几个区域,一个区域一个区域地整理呀,分治法就类似这个道理。
比如说归并排序,它就是把这堆数字不断地分成两半,再把两半合起来,就像你把房间先分成左边和右边,分别整理好后再合到一起。
再说说在图像识别里的应用。
假如你面前有一张超级复杂的图片,里面有各种形状、各种颜色的东西,哇,这要怎么搞清楚啊!但用了分治法,就像是把这张图片切成小块,一块一块地去识别、理解。
就好像你认识一个新朋友,你会先看他的脸,再看他的衣服,一步一步慢慢了解,对吧?
还有啊,在解决复杂的计算问题时,分治法也能大显身手。
好比你要算一道超级复杂的数学题,直接去算可能会让你头大,但是通过分治法,把问题分成小份,逐个击破。
就像你打游戏,一个大 boss 你一下打不过,那就一点一点地削弱它呀!
分治法不就是这样神奇而好用的东西吗?它能把超级复杂、看似不可能完成的任务,变得有条有理,能够被我们一步一步地解决掉。
所以说呀,分
治法真的是我们的好帮手,难道不是吗?它就像一把神奇的钥匙,能打开那些看似紧闭的难题大门,让我们在解决问题的道路上一路畅通无阻!这就是分治法的厉害之处,大家可千万别小瞧它哟!。
分治法练习题

分治法练习题分治法是一种常见的算法设计方法,其核心思想是将问题划分成若干个规模较小且结构相似的子问题,然后分别解决这些子问题,最后将子问题的结果合并得到原问题的解。
在实际应用中,选取合适的问题划分方式以及合并子问题的结果是非常关键的。
下面,我将为您介绍两个分治法的练习题。
题目一:寻找最大子数组和给定一个整数数组,找到其连续子数组中的最大和。
例如,输入数组[-2, 1, -3, 4, -1, 2, 1, -5, 4],其最大子数组和为6,对应的子数组为[4, -1, 2, 1]。
解题思路:1. 将原问题划分成规模较小的子问题:将数组分为两部分,分别求解左子数组和右子数组的最大子数组和,以及跨越中点的最大子数组和。
2. 递归求解子问题:对于左右子数组,可以再次使用分治法求解;对于跨越中点的最大子数组和,可以通过以中点为中心,向左右扩展来得到。
3. 合并子问题的结果:对于左右子数组的最大子数组和,取较大值作为整个数组的最大子数组和;对于跨越中点的最大子数组和,取两边相加的最大值。
题目二:求解逆序对个数给定一个数组,逆序对是指数组中两个元素a[i]和a[j],满足i < j且a[i] > a[j]。
请设计一个算法,求解给定数组中逆序对的个数。
解题思路:1. 将原问题划分成规模较小的子问题:将数组平均分为两部分,分别求解左子数组和右子数组中逆序对的个数,以及两个子数组之间的逆序对个数。
2. 递归求解子问题:对于左右子数组,可以再次使用分治法求解;对于两个子数组之间的逆序对个数,可以通过归并排序的思想来求解。
3. 合并子问题的结果:将左右子数组合并为一个有序数组,并统计两个子数组之间的逆序对个数。
同时,递归返回的结果也需要累加进逆序对的总数。
通过以上两个练习题,我们可以更加深入地理解和应用分治法这一算法设计思想,同时也能提升对问题分解和结果合并的能力。
当然,在实际应用中,我们需要灵活运用分治法以及结合具体问题来设计合适的算法,并注意算法的效率和性能。
分治法列题c++

分治法列题c++分治法是一种算法设计思想,它将一个问题分解为多个相似的子问题,然后逐个解决子问题,最后将这些子问题的解合并起来得到原问题的解。
分治法通常采用递归的方式实现。
下面是一个使用分治法解决问题的示例,将一个数组排序。
```cpp#include <iostream>#include <vector>using namespace std;vector<int> merge(vector<int>& left, vector<int>& right) {vector<int> result;int i = 0, j = 0;while (i < left.size() && j < right.size()) {if (left[i] < right[j]) {result.push_back(left[i]);i++;} else {result.push_back(right[j]);j++;}}while (i < left.size()) {result.push_back(left[i]);i++;while (j < right.size()) {result.push_back(right[j]);j++;}return result;}vector<int> mergeSort(vector<int>& nums) {if (nums.size() <= 1) {return nums;}int mid = nums.size() / 2;vector<int> left(nums.begin(), nums.begin() + mid); vector<int> right(nums.begin() + mid, nums.end()); left = mergeSort(left);right = mergeSort(right);return merge(left, right);}int main() {vector<int> nums = {5, 2, 8, 6, 1, 9, 3, 7, 4};vector<int> sortedNums = mergeSort(nums);for (int num : sortedNums) {cout << num << " ";}return 0;}```在这个示例中,我们使用了两个函数 `merge` 和 `mergeSort` 来实现归并排序。
【分治算法】木材加工,解读

【分治算法】木材加工,解读
【实用版】
目录
1.分治算法简介
2.木材加工问题的提出
3.分治算法在木材加工问题中的应用
4.结论
正文
【分治算法】
分治算法是一种将复杂问题分解为多个简单问题来求解的算法思想。
这种算法通过将大问题拆分成多个小问题,然后逐个解决这些小问题,最后将这些小问题的解合并,从而得到原大问题的解。
分治算法广泛应用于各种领域的问题中,例如木材加工问题。
【木材加工问题的提出】
在木材加工业中,有一个经典问题:如何将一根圆木加工成若干段长度相等的木条,同时要求加工次数最少?这个问题可以通过分治算法来解决。
【分治算法在木材加工问题中的应用】
假设圆木的长度为 L,我们需要将其加工成 n 段长度相等的木条。
我们可以将这个问题分解为两个子问题:将圆木加工成 n/2 段长度相等的木条,以及将圆木加工成 n/2+1 段长度相等的木条。
然后,我们再将这两个子问题继续分解,直到子问题的规模足够小,可以用简单的方法求解。
当子问题的规模足够小时,我们可以直接计算出每段木条的长度。
然后,通过将这些长度依次排列,我们可以得到原问题的解。
对于较大的子
问题,我们可以通过递归的方式调用分治算法来求解。
最后,将各个子问题的解合并,即可得到原问题的解。
【结论】
分治算法在木材加工问题中的应用,展示了分治算法在解决实际问题中的强大功能。
分治算法的例子

分治算法的例子1. 哎呀,你知道吗,比如有一个大任务是把一堆杂乱的数字排序。
这就好像整理一个超级乱的房间一样。
我们可以把这堆数字分成两部分,分别排序,然后再合起来,这就是分治算法呀!就像你先整理房间的左边,再整理右边,最后整个房间就整齐啦!2. 嘿,想象一下要在一个巨大的图书馆里找一本书。
我们可以把图书馆分成几个区域,每个区域再派人去找,这也是分治算法呀!难道不是很神奇吗?就像大家分工合作去找那本神秘的书。
3. 哇哦,你看计算一个很大很大的矩阵的乘法。
这简直像一座难以翻越的大山!但我们可以把它分成小块,分别计算,再组合起来,这不就是分治算法的魅力吗?就如同一点点攻克一座高山。
4. 你想想,要解决一个超级复杂的迷宫问题。
我们可以把迷宫分成几个部分呀,一部分一部分地去探索,然后汇总结果,这不是分治算法在起作用吗?这多像一点一点解开迷宫的秘密呀!5. 嘿呀,比如统计一个很大区域里的人口数量。
我们可以把这个区域划分成小块,分别统计,最后汇总,这就是分治算法呀!跟把一个大蛋糕切成小块来数有什么区别呢!6. 哎呀呀,要找出一堆物品中最重的那个。
我们可以把物品分成几组,找出每组最重的,再比较,这不就是用了分治算法嘛!是不是很像在一堆宝藏中找最耀眼的那颗宝石呀!7. 哇塞,要对一个超级长的字符串进行操作。
那我们就把它分成小段来处理嘛,这就是分治算法的精彩之处呀!好比把一条长长的绳子分段来摆弄。
8. 你瞧,像解决一个大的图像识别问题。
我们把图像分成小部分,一部分一部分地去分析识别,最后拼起来,这绝对是分治算法的厉害所在!就如同一片片拼凑出一幅美丽的图画。
我的观点结论就是:分治算法真的是超厉害的,它能把复杂的大问题化简,就像一把神奇的钥匙能打开很多难题的大门!。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
inttmp=a;
a=b;
b=tmp;
}
//本函数求arr[p:q]的一个划分i,使arr[p:i-1]都小于arr[i],arr[i+1,q]都大于arr[i]
intpartition(int*arr,intp,intq)
{
intindex=p-1,start=p,base=arr[q];
for(;start<q;start++)
{
res+=abs(midx-x[i]);
res+=abs(midy-y[i]);
}
// 输出结果
printf("%I64d\n",res);
}
return0;
}
1034
解题思路:
递推公式如下:
F (n,m) =m*F (n −1,m) +F (n −1,m −1)
F(n,m)表示把n个元素的集合分为m个子集,有多少种分法。
{
longlongsum= 0;
//计算subset(n,i)之和
for(inti=1;i<=n;i++)
{
sum+=subset(n,i);
}
printf("%I64d\n",sum);
}
return0;
}
{
if(arr[start]<=base)
{
swap(arr[start],arr[++index]);
}
}
swap(arr[++index],arr[q]);
returnindex;
}
//快速排序
voidqsort(int*arr,intp,intq)
{
if(p<=q)
{
intpos=partition(arr,p,q);
}
//排序
qsort(arr,0,n-1);
//取中位数的下标mid,然后计算距离
longlongsum= 0;
intmid=arr[n/2];
for(inti=0;i<n;i++)
{
sum+=abs(mid-arr[i]);
}
printf("%I64d\n",sum);
}
return0;
}
1032
求中位数的方法可以用排序后取a[(left+right)/2],当然更推荐用书上的线性时间选择算法解决。记求得的主管道为 ,最后要输出的结果只需要计算: ,输出即可。
另外要提醒的是本题多Case。
程序
#include<stdio.h>
#include<stdlib.h>
voidswap(int&a,int&b)
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
usingnamespacestd;
intx[10000],y[10000];
intmain()
{
intn;
while(scanf("%d",&n)!=EOF)
{
//读取x 和y 坐标
for(inti=0;i<n;i++)
解题思路
本题和上一题非常类似,这次是要找出在居民点中邮局的最佳位置。很容易想到,这次不仅要确定y坐标,还要确定x坐标。类似猜想可以知道,邮局最佳位置 应该为:
等于所有居民点x坐标的中位数;
等于所有居民点y坐标的中位数;
分别求中位数的过程和上题类似,不再陈述。最终的计算结果:要求距离之和,输出 。
程序源
}
return0;
}
1033
解题思路
假定F(n,m)表示整数n的m划分,则整数n的所有划分是: 。
提醒:
1)由于本题数据范围比较大,需要用64位长整型即__int64或者long long。
2)如果n比较大的话,递归算法计算时间会比较长,最好用动态规划算法实现。
程序源代码
#include<stdio.h>
{
if(m==1 ||m==n)
{
return1;
}
else
{
returndivide(n-1,m-1)+m*divide(n-1,m);
}
}
intmain()
{
intn,m;
//多case 输入
while(scanf("%d%d",&n,&m)!=EOF)
{
printf("%I64d\n",divide(n,m));
{
scanf("%d %d",&x[i],&y[i]);
}
//调用STL中的排序算法,分别对x 坐标和y 坐标进行排序
sort(x,x+n);
sort(y,y+n);
//取x,y 坐标的中位数并计算距离
intmidx=x[n/2];
intmidy=y[n/2];
longlongres= 0;
for(inti= 0;i<n;i++)
1031
解题思路
本题目可以分为两个步骤:
1、找出主管道的位置;
2、根据主管道的位置,计算各个油井到主管道的长度之和。
根据题意,设主管道贯穿东西,与y轴平行。而各个子油井则分布在主输油管道的上下两侧。如下图:
由上图,其实只需要确定主管道的y坐标,而与各个子油井的x坐标无关!根据猜测,易知:主管道的y坐标就是所有子油井y坐标的中位数。(可以用平面几何知识证明,略)
qsort(arr,p,pos-1);
qsort(arr,pos+1,q);
}
}
intarr[10001];
intmain()
{
intn;
//注意多case 写法
while(scanf("%d",&n)!=EOF)
{
//读取数据
for(inti=0;i<n;i++)
{
//读取y
scanf("%d %d",&arr[i],&arr[i]);
如果要求F(4,2)该怎么办呢?
A.往①里添一个元素{4},得到{{1,2,3},{4}}
B.往②里的任意一个子集添一个4,得到
{{1,2,4},{3}},{{1,2},{3,4}},
{{1,3,4},{2}},{{1,3},{2,4}},
{{2,3,4},{1}},{{2,3},{1,4}}
∴F(4,2)=F(3,1)+2*F(3,2)=1+2*3=7
推广,得F(n,m)=F(n-1,m-1)+m*F(n-1,m)
提醒:由于本题数据范围比较大,需要用64位长整型即__int64或者long long。
程序源代码:
#include<stdio.h>
//计算把含有n 个元素的集合分割为m 个子集,有多少种解决方案
longlongdivide(intn,intm)
证明如下:
n个元素的集合可以划分为F(n,m)个不同的由m个非空子集组成的集合。
考虑3个元素的集合,可划分为
①1个子集的集合:{{1,2,3}}
②2个子集的集合:{{1,2},{3}},{{1,3},{2}},{{2,3},{1}}
③3个子集的集合:{{1},{2},{3}}
∴F(3,1)=1;F(3,2)=3;F(3,3)=1;
//计算把含有n 个元素的集合分割为m 个子集,有多少种解决方案
longlongsubset(intn,intm)
{
if(n==m||m== 1)
{
retuurnsubset(n-1,m-1) +m*subset(n-1,m);
}
}
intmain()
{
intn;
while(scanf("%d",&n) !=EOF)