貪心算法 --- 馬踏棋盤(C)

五大常用算法之一,好高大上的東西,決定了把這五大算法給搞了,第一個貪心算法,百度一下它的概念,貼過了來了:

貪心算法(又稱貪婪算法)是指,在對問題求解時,總是做出在當前看來是最好的選擇。也就是說,不從整體最優上加以考慮,他所做出的僅是在某種意義上的局部最優解。貪心算法不是對所有問題都能得到整體最優解,但對範圍相當廣泛的許多問題他能產生整體最優解或者是整體最優解的近似解。

貪婪算法是一種改進了的分級處理方法。其核心是根據題意選取一種量度標準。然後將這多個輸入排成這種量度標準所要求的順序,按這種順序依次輸入一個量。如果這個輸入和當前已構成在這種量度意義下的部分最佳解加在一起不能產生一個可行解,則不把此輸入加到這部分解中。這種能夠得到某種量度意義下最優解的分級處理方法稱爲貪婪算法。
對於一個給定的問題,往往可能有好幾種量度標準。初看起來,這些量度標準似乎都是可取的,但實際上,用其中的大多數量度標準作貪婪處理所得到該量度意義下的最優解並不是問題的最優解,而是次優解。因此,選擇能產生問題最優解的最優量度標準是使用貪婪算法的核心。
一般情況下,要選出最優量度標準並不是一件容易的事,但對某問題能選擇出最優量度標準後,用貪婪算法求解則特別有效。最優解可以通過一系列局部最優的選擇即貪婪選擇來達到,根據當前狀態做出在當前看來是最好的選擇,即局部最優解選擇,然後再去解做出這個選擇後產生的相應的子問題。每做一次貪婪選擇就將所求問題簡化爲一個規模更小的子問題,最終可得到問題的一個整體最優解。
看完了概念,好像還是有點模糊,用一個例子來更深層次的理解下吧。
馬踏棋盤問題:
思路:首先將棋盤當做一個二維數組,然後將數組的每一個值賦值爲0,將馬的起始地址設爲1,比如說是5,6,然後判斷當前馬所能夠到達的地方,找出這些點,通過一個數組,數組中的值記錄馬下一個位置和當前位置之間的差值,然後遍歷一遍數組,檢測其下一個位置是否還在棋盤之中,是否走過該位置,然後將其走過的點記錄下來,將所獲得的這些點的記錄進行遍歷,遍歷每一個點,看一下這些點是否還可以繼續向下走,然後將這些點可以向下走的位置有幾個記錄下來,找到路徑最少的一個點,然後將這個點設爲初始點,進入下一次遍歷,這裏個一個問題就是爲什麼要找剩餘路徑最少的點呢?而不是去找路徑多的,或者是隨機的呢,原因就是路勁少的點,到達它的點就少了,如果這次沒有選擇這個點的話,下次再來這個點就會變得困難,甚至是不可以走了。開始寫的一個老是走到一半就會停止了,不能夠繼續前進了,不知道是什麼原因,搞了一晚上的時間,有種把電腦砸了的衝動,最終發現原因竟然是那麼簡單的,很基礎的一點關於c的問題,看來c好久不看,確實是不可以了。
實現代碼實例:
# include <stdio.h>
# include <stdlib.h>
# include <math.h>
# define ROWS 8
# define COLS 8
int cheesboard [ROWS] [COLS];
const int moveX [8] = {-2,-1,1,2,2,1,-1,-2};
const int moveY [8] = {1,2,2,1,-1,-2,-2,-1};
//初始化棋盤,將棋盤所有的位置賦值爲0
void initBoard (int board[][COLS]){
    int i ,j;
	for(i = 0; i < 8; i ++){
		for( j = 0; j < 8; j ++){
			board[i][j] = 0;
		}
	}
}
//從待選的下一個點的集合中路徑最短的一個
int getMinPath (int a[],int num){
    //定義下標爲
	int i = 0,index=0;
	//定義最小的值爲a【0】,找到最小的值,而且大於0的值
    int min= a[0];
    for(i = 0 ; i< num; i++)
    {
        if(a[i] >0)
        {
            min = a[i];
            index = i;
            break;
        }
    }
    for(i = index + 1; i < num ;  i++)
    {
        if(a[i] > 0 && min > a[i])
        {
            min = a[i];
            index = i;
        }
    }
    if(a[index] > 0)
        return index;
    return -1;
}
// 打印路徑
void printPath (int board[][COLS]){
    int i,j;
	for (i = 0; i < ROWS; i++){
		for ( j = 0; j < COLS; j++){
			printf("%d\t",board[i][j]);
		}
		printf("\n\n");
	}
}
// 獲得馬行走的路徑
void getPath (int board [][COLS], int startX, int startY){
    //下一個可行位置的數目
	int next = 0;
	//路徑最短的可行位置在數組中的位置
	int min;
	//下一個可行位置的可行位置數目
	int nextNext;
	//將棋盤初始化
	initBoard (board);
	// 存放下一個位置對應的下一個位置的數目
    int nextNum[8] = {0,0,0,0,0,0,0,0};
    //下一個位置的在二維數組中對應位置,初始爲0
	int nextX[8] = {0,0,0,0,0,0,0,0};
    int nextY[8] = {0,0,0,0,0,0,0,0};
    //第一個位置賦值爲1
	board [startX] [startY] = 1;
	int m,i,j;
	//走完所有的點要循環63次
	for ( m = 1; m < 64; m++){
            //當前點其後面可行的位置設爲0
            next = 0;
    //通過循環來判斷該位置是否還可以向下面位置移動
	for ( i =  0; i < 8; i++){
		if(startX + moveX[i] < 0 || startX + moveX[i] >= ROWS
			|| startY + moveY[i] < 0 || startY + moveY[i] >= COLS
			|| board[startX+moveX[i]][startY+moveY[i]] != 0){
			    continue;
			}
			//如果可以向下一個位置移動的話,通過next數組保存下來,通過next記錄下有多少個
		nextX [next] = startX + moveX[i];
		nextY [next] = startY + moveY[i];
		next ++;
	}
	//循環結束之後,對next的值進行判斷,當爲1的時候
	if (next == 1){
        //讓min=0,表示現在所需要的位置是在我們的保存next數組中的第一位
		min = 0;
    //設置初始點
		goto set_nextpoint;
	}
	//無法向下一個位置移動了
	else if (next == 0){
		printf("沒有路徑可走了\n");
		goto print_path;
	}
	else {
            /*當有多個路徑可以走的時候,檢測每一個點還可不可以繼續向下走然後
            記錄下來該點有幾個點可以向下走,找到最少的一個但是不爲0的哪一個
            */
		for (i = 0; i<next; i++){
			nextNext = 0;
			for(j = 0; j < 8; j++){
				if(nextX[i] + moveX[j] >=0 && nextX[i] + moveX[j] < ROWS
					&& nextY[i] + moveY[j] >= 0 && nextY[i] + moveY[j] <COLS
					&& board [nextX[i]+moveX[j]][nextY[i]+moveY[j]] == 0){
					    nextNext ++;
					}

			}
			nextNum[i] = nextNext;
		}
		if ((min = getMinPath(nextNum,next))>=0 )
		{
			goto set_nextpoint;
		}
		else{
			printf("沒有路徑可走了\n");
			goto print_path;
		}
	}
set_nextpoint:
        startX = nextX[min];
        startY = nextY[min];
        board[startX][startY] = m+1;
	}
print_path:
	printPath(board);

}
int main (){
	int i, j;
	//獲得最開始的位置
	printf ("請輸入第一個棋子所在位置\n");
	scanf ("%d%d" , & i , & j);
	printf("%d,%d",i,j);
	//調用該函數獲取路徑
	getPath(cheesboard, i, j);
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章