分治演算法球隊
A. 如何理解分治演算法及相關例題
演算法步驟:
1 :從左上角起,給棋盤編號(1,1),(1,2)(8,8),計為集合qp。tracks記錄走過的每個點. (可以想像為坐標(x,y))
2:設起點為(1,1),記為 當前位置 cp,
3:搜索所有可走的下一步,根據「馬行日」的走步規則,可行的點的坐標是x坐標加減1,y坐標加減2,
或是x加減2,y加減1; (例如起點(1,1),可計算出(1+1,1+2),(1+1,1-2),(1-1,1+2),(1-1,1-2),(1+2,1+1),(1+2,1-1),(1-2,1+1),(1-2,1-1) 共8個點), 如果沒有搜到可行點,程序結束。
4:判斷計算出的點是否在棋盤內,即是否在集合qp中;判斷點是否已經走過,即是否在集合tracts中,不在才是合法的點。(在上面的舉例起點(1,1),則合法的下一步是(2,3)和 (3,2))
5:將前一步的位置記錄到集合tracts中,即tracts.add(cp);選擇一個可行點,cp=所選擇點的坐標。
6:如果tracts里的點個數等於63,退出程序,否則回到步驟3繼續執行。
B. 分治法的步驟
分治法在每一層遞歸上都有三個步驟:
分解:將原問題分解為若干個規模較小,相互獨立,與原問題形式相同的子問題;
解決:若子問題規模較小而容易被解決則直接解,否則遞歸地解各個子問題;
合並:將各個子問題的解合並為原問題的解。
它的一般的演算法設計模式如下:
Divide-and-Conquer(P)
1. if |P|≤n0
2. then return(ADHOC(P))
3. 將P分解為較小的子問題 P1 ,P2 ,...,Pk
4. for i←1 to k
5. do yi ← Divide-and-Conquer(Pi) △ 遞歸解決Pi
6. T ← MERGE(y1,y2,...,yk) △ 合並子問題
7. return(T)
其中|P|表示問題P的規模;n0為一閾值,表示當問題P的規模不超過n0時,問題已容易直接解出,不必再繼續分解。ADHOC(P)是該分治法中的基本子演算法,用於直接解小規模的問題P。因此,當P的規模不超過n0時直接用演算法ADHOC(P)求解。演算法MERGE(y1,y2,...,yk)是該分治法中的合並子演算法,用於將P的子問題P1 ,P2 ,...,Pk的相應的解y1,y2,...,yk合並為P的解。
根據分治法的分割原則,原問題應該分為多少個子問題才較適宜?
各個子問題的規模應該怎樣才為適當?
答: 但人們從大量實踐中發現,在用分治法設計演算法時,最好使子問題的規模大致相同。換句話說,將一個問題分成大小相等的k個子問題的處理方法是行之有效的。許多問題可以取 k = 2。這種使子問題規模大致相等的做法是出自一種平衡(balancing)子問題的思想,它幾乎總是比子問題規模不等的做法要好。
出處:網路
實踐題目:
給定一個順序表,編寫一個求出其最大值和最小值的分治演算法。
分析:
由於順序表的結構沒有給出,作為演示分治法這里從簡順序表取一整形數組數組大小由用戶定義,數據隨機生成。我們知道如果數組大小為 1 則可以直接給出結果,如果大小為 2則一次比較即可得出結果,於是我們找到求解該問題的子問題即: 數組大小 <= 2。到此我們就可以進行分治運算了,只要求解的問題數組長度比 2 大就繼續分治,否則求解子問題的解並更新全局解
以下是代碼。
*/
/*** 編譯環境TC ***/
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#define M 40
/* 分治法獲取最優解 */
void PartionGet(int s,int e,int *meter,int *max,int *min){
/* 參數:
* s 當前分治段的開始下標
* e 當前分治段的結束下標
* meter 表的地址
* max 存儲當前搜索到的最大值
* min 存儲當前搜索到的最小值
*/
int i;
if(e-s <= 1){ /* 獲取局部解,並更新全局解 */
if(meter[s] > meter[e]){
if(meter[s] > *max)
*max = meter[s];
if(meter[e] < *min)
*min = meter[e];
}
else{
if(meter[e] > *max)
*max = meter[e];
if(meter[s] < *min)
*min = meter[s];
}
return ;
}
i = s + (e-s)/2; /* 不是子問題繼續分治,這里使用了二分,也可以是其它 */
PartionGet(s,i,meter,max,min);
PartionGet(i+1,e,meter,max,min);
}
int main(){
int i,meter[M];
int max = INT_MIN; /* 用最小值初始化 */
int min = INT_MAX; /* 用最大值初始化 */
printf(The array's element as followed:
);
rand(); /* 初始化隨機數發生器 */
for(i = 0; i < M; i ++){ /* 隨機數據填充數組 */
meter[i] = rand()%10000;
if(!((i+1)%10)) /* 輸出表的隨機數據 */
printf(%-6d
,meter[i]);
else
printf(%-6d,meter[i]);
}
PartionGet(0,M - 1,meter,&max,&min); /* 分治法獲取最值 */
printf(
Max : %d
Min : %d
,max,min);
system(pause);
return 0;
}
C. 分治演算法的基本思想
當我們求解某些問題時,由於這些問題要處理的數據相當多,或求解過程相當復雜,使得直接求解法在時間上相當長,或者根本無法直接求出。對於這類問題,我們往往先把它分解成幾個子問題,找到求出這幾個子問題的解法後,再找到合適的方法,把它們組合成求整個問題的解法。如果這些子問題還較大,難以解決,可以再把它們分成幾個更小的子問題,以此類推,直至可以直接求出解為止。這就是分治策略的基本思想。
D. 使用分治演算法解決的問題具備什麼特徵
分治法能解決的問題一般具有以下幾個特徵:
1、該問題的規模縮小到一定的程度就可以容易的解決。
2、該問題可以分解為若干個規模較小的相同問題,即該問題具有最優子結構性質。
3、利用該問題分解出的子問題的解可以合並為該問題的解。
4、該問題所分解出的自問題是相互獨立的,即子問題之間不包含子子問題。
(4)分治演算法球隊擴展閱讀
思想及策略
分治演算法的設計思想是:將一個難以直接解決的大問題,分割成一些規模小的相同的問題,一邊各個擊破,分而治之。
分治演算法的策略是:對於一個規模為n的問題,若該問題可以容易地解決(比如規模n比較小)則直接解決,否則將其分解成k個規模較小的自問題,這些子問題相互獨立且與元問題形式相同,遞歸地解這些子問題,然後將各子問題的解合並得到原問題的解。
E. 分治演算法和動態規劃的區別和聯系
一、分治法與動態規劃主要共同點:
1)二者都要求原問題具有最優子結構性質,都是將原問題分而治之,分解成若干個規模較小(小到很容易解決的程序)的子問題。然後將子問題的解合並,形成原問題的解。
二、分治法與動態規劃實現方法:
① 分治法通常利用遞歸求解。
② 動態規劃通常利用迭代法自底向上求解,但也能用具有記憶功能的遞歸法自頂向下求解。
三、分治法與動態規劃主要區別:
① 分治法將分解後的子問題看成相互獨立的。
② 動態規劃將分解後的子問題理解為相互間有聯系,有重疊部分。
F. 跪求:循環日程表的分治法程序!
#include "stdio.h"
int a[10][10];
void tourna(int n)
{
if( n == 1 )
{
a[1][1] = 1;
return;
}
if (n%2!=0)
{ tourna (n+1);
return;}
tourna(n/2);
make(n);
}
make(int n)
{ if( (n/2>1) && (n%2!=0))
odd(n);
else
(n);
}
(int n)
{int i,j;
int m = n/2;
for( i = 1 ; i <= m ; i++ )
{
for( j = 1 ; j <= m ; j++ )
{
a[i][j + m] = a[i][j] + m;
a[i + m][j] = a[i][j + m];
a[i + m][j + m] = a[i][j];
}
}
}
odd(int n)
{ int m=n/2,b[100],i,j;
for ( i = 1 ; i <= m ; i++ )
b[i]=m+i;b[m+i]=b[i];
for( i = 1 ; i <= m ; i++ )
for ( j = 1 ; j <= m+1 ; j++ )
{ if (a[i][j]>m)
{ a[i][j]=b[i];a[m+i][j]=(b[i]+m)%n;}
else
a[m+i][j]=a[i][j]+m;
for ( i = 2 ; i <= m ; i++ )
a[i][m+j]=b[i+j-1];
a[b[i+j-1]][m+j]=i;
}}
main()
{ int n = 1,i,j;
int k;
scanf("%d",&n);
tourna(n);
for( i = 1 ; i <= n ; i++ )
{
for( j = 1 ; j <= n ; j++ )
{
printf("%d ", a[i][j]);
}
printf("\n");
}
}
G. 分治演算法的應用實例
下面通過實例加以說明: 給你一個裝有1 6個硬幣的袋子。1 6個硬幣中有一個是偽造的,並且那個偽造的硬幣比真的硬幣要輕一些。你的任務是找出這個偽造的硬幣。為了幫助你完成這一任務,將提供一台可用來比較兩組硬幣重量的儀器,利用這台儀器,可以知道兩組硬幣的重量是否相同。比較硬幣1與硬幣2的重量。假如硬幣1比硬幣2輕,則硬幣1是偽造的;假如硬幣2比硬幣1輕,則硬幣2是偽造的。這樣就完成了任務。假如兩硬幣重量相等,則比較硬幣3和硬幣4。同樣,假如有一個硬幣輕一些,則尋找偽幣的任務完成。假如兩硬幣重量相等,則繼續比較硬幣5和硬幣6。按照這種方式,可以最多通過8次比較來判斷偽幣的存在並找出這一偽幣。
另外一種方法就是利用分而治之方法。假如把1 6硬幣的例子看成一個大的問題。第一步,把這一問題分成兩個小問題。隨機選擇8個硬幣作為第一組稱為A組,剩下的8個硬幣作為第二組稱為B組。這樣,就把1 6個硬幣的問題分成兩個8硬幣的問題來解決。第二步,判斷A和B組中是否有偽幣。可以利用儀器來比較A組硬幣和B組硬幣的重量。假如兩組硬幣重量相等,則可以判斷偽幣不存在。假如兩組硬幣重量不相等,則存在偽幣,並且可以判斷它位於較輕的那一組硬幣中。最後,在第三步中,用第二步的結果得出原先1 6個硬幣問題的答案。若僅僅判斷硬幣是否存在,則第三步非常簡單。無論A組還是B組中有偽幣,都可以推斷這1 6個硬幣中存在偽幣。因此,僅僅通過一次重量的比較,就可以判斷偽幣是否存在。
假設需要識別出這一偽幣。把兩個或三個硬幣的情況作為不可再分的小問題。注意如果只有一個硬幣,那麼不能判斷出它是否就是偽幣。在一個小問題中,通過將一個硬幣分別與其他兩個硬幣比較,最多比較兩次就可以找到偽幣。這樣,1 6硬幣的問題就被分為兩個8硬幣(A組和B組)的問題。通過比較這兩組硬幣的重量,可以判斷偽幣是否存在。如果沒有偽幣,則演算法終止。否則,繼續劃分這兩組硬幣來尋找偽幣。假設B是輕的那一組,因此再把它分成兩組,每組有4個硬幣。稱其中一組為B1,另一組為B2。比較這兩組,肯定有一組輕一些。如果B1輕,則偽幣在B1中,再將B1又分成兩組,每組有兩個硬幣,稱其中一組為B1a,另一組為B1b。比較這兩組,可以得到一個較輕的組。由於這個組只有兩個硬幣,因此不必再細分。比較組中兩個硬幣的重量,可以立即知道哪一個硬幣輕一些。較輕的硬幣就是所要找的偽幣。 在n個元素中找出最大元素和最小元素。我們可以把這n個元素放在一個數組中,用直接比較法求出。演算法如下:
void maxmin1(int A[],int n,int *max,int *min)
{ int i;
*min=*max=A[0];
for(i=0;i <= n;i++)
{ if(A[i]> *max) *max= A[i];
if(A[i] < *min) *min= A[i];
}
}
上面這個演算法需比較2(n-1)次。能否找到更好的演算法呢?我們用分治策略來討論。
把n個元素分成兩組:
A1={A[1],...,A[int(n/2)]}和A2={A[INT(N/2)+1],...,A[N]}
分別求這兩組的最大值和最小值,然後分別將這兩組的最大值和最小值相比較,求出全部元素的最大值和最小值。如果A1和A2中的元素多於兩個,則再用上述方法各分為兩個子集。直至子集中元素至多兩個元素為止。
例如有下面一組元素:-13,13,9,-5,7,23,0,15。用分治策略比較的演算法如下:
void maxmin2(int A[],int i,int j,int *max,int *min)
/*A存放輸入的數據,i,j存放數據的范圍,初值為0,n-1,*max,*min 存放最大和最小值*/
{ int mid,max1,max2,min1,min2;
if (j==i) {最大和最小值為同一個數;return;}
if (j-1==i) {將兩個數直接比較,求得最大會最小值;return;}
mid=(i+j)/2;
求i~mid之間的最大最小值分別為max1,min1;
求mid+1~j之間的最大最小值分別為max2,min2;
比較max1和max2,大的就是最大值;
比較min1和min2,小的就是最小值;
} 題目:在一個(2^k)*(2^k)個方格組成的棋盤上,有一個特殊方格與其他方格不同,稱為特殊方格,稱這樣的棋盤為一個特殊棋盤。我們要求對棋盤的其餘部分用L型方塊填滿(註:L型方塊由3個單元格組成。即圍棋中比較忌諱的愚形三角,方向隨意),且任何兩個L型方塊不能重疊覆蓋。L型方塊的形態如下:
題目的解法使用分治法,即子問題和整體問題具有相同的形式。我們對棋盤做一個分割,切割一次後的棋盤如圖1所示,我們可以看到棋盤被切成4個一樣大小的子棋盤,特殊方塊必定位於四個子棋盤中的一個。假設如圖1所示,特殊方格位於右上角,我們把一個L型方塊(灰色填充)放到圖中位置。這樣對於每個子棋盤又各有一個「特殊方塊」,我們對每個子棋盤繼續這樣分割,直到子棋盤的大小為1為止。
用到的L型方塊需要(4^k-1)/3 個,演算法的時間是O(4^k),是漸進最優解法。
本題目的C語言的完整代碼如下(TC2.0下調試),運行時,先輸入k的大小,(1<=k<=6),然後分別輸入特殊方格所在的位置(x,y), 0<=x,y<=(2^k-1)。 #include<stdio.h>//#include<conio.h>//#include<math.h>inttitle=1;intboard[64][64];voidchessBoard(inttr,inttc,intdr,intdc,intsize){ints,t;if(size==1)return;t=title++;s=size/2;if(dr<tr+s&&dc<tc+s)chessBoard(tr,tc,dr,dc,s);else{board[tr+s-1][tc+s-1]=t;chessBoard(tr,tc,tr+s-1,tc+s-1,s);}if(dr<tr+s&&dc>=tc+s)chessBoard(tr,tc+s,dr,dc,s);else{board[tr+s-1][tc+s]=t;chessBoard(tr,tc+s,tr+s-1,tc+s,s);}if(dr>=tr+s&&dc<tc+s)chessBoard(tr+s,tc,dr,dc,s);else{board[tr+s][tc+s-1]=t;chessBoard(tr+s,tc,tr+s,tc+s-1,s);}if(dr>=tr+s&&dc>=tc+s)chessBoard(tr+s,tc+s,dr,dc,s);else{board[tr+s][tc+s]=t;chessBoard(tr+s,tc+s,tr+s,tc+s,s);}}voidmain(){intdr=0,dc=0,s=1,i=0,j=0;printf(printinthesizeofchess:
);scanf(%d,&s);printf(printinspecalpointx,y:
);scanf(%d%d,&dr,&dc);if(dr<s&&dc<s){chessBoard(0,0,dr,dc,s);for(i=0;i<s;i++){for(j=0;j<s;j++){printf(%4d,board[i][j]);}printf(
);}}elseprintf(thewrongspecalpoint!!
);getch();}
H. 什麼是分治演算法
分治法就是將一個復雜的問題分成多個相對簡單的獨立問題進行求解,並且綜合所有簡單問題的解可以組成這個復雜問題的解。
例如快速排序演算法就是一個分治法的例子。即將一個大的無序序列排序成有序序列,等於將兩個無序的子序列排序成有序,且兩個子序列之間滿足一個序列的元素普遍大於另一個序列中的元素。
I. 分治演算法的解題步驟
分治法解題的一般步驟:
(1)分解,將要解決的問題劃分成若干規模較小的同類問題;
(2)求解,當子問題劃分得足夠小時,用較簡單的方法解決;
(3)合並,按原問題的要求,將子問題的解逐層合並構成原問題的解。
J. 分治演算法
演算法步驟:
1 :從左上角起,給棋盤編號(1,1),(1,2),。。。。。。(8,8),計為集合qp。tracks記錄走過的每個點. (可以想像為坐標(x,y))
2:設起點為(1,1),記為 當前位置 cp,
3:搜索所有可走的下一步,根據「馬行日」的走步規則,可行的點的坐標是x坐標加減1,y坐標加減2,
或是x加減2,y加減1; (例如起點(1,1),可計算出(1+1,1+2),(1+1,1-2),(1-1,1+2),(1-1,1-2),(1+2,1+1),(1+2,1-1),(1-2,1+1),(1-2,1-1) 共8個點), 如果沒有搜到可行點,程序結束。
4:判斷計算出的點是否在棋盤內,即是否在集合qp中;判斷點是否已經走過,即是否在集合tracts中,不在才是合法的點。(在上面的舉例起點(1,1),則合法的下一步是(2,3)和 (3,2))
5:將前一步的位置記錄到集合tracts中,即tracts.add(cp);選擇一個可行點,cp=所選擇點的坐標。
6:如果tracts里的點個數等於63,退出程序,否則回到步驟3繼續執行。