1. 程式人生 > >資源分配問題

資源分配問題

一、問題描述

已知一塊土地被劃分成6*6的網格,在這些網格中共有4個煤礦和4個鐵礦,每個礦藏佔一格。要將這些資源平均分配給四個公司A,B,C,D進行開發(每個公司開發一個煤礦和一個鐵礦),同時要將這塊土地平分成四塊給這四個公司管理。為了集中管理,中心的四個網格已經分給四個公司建立總控站,每個公司分得的土地必須都與本公司的總控站連通,且土地只能沿網格線進行劃分,四塊土地的大小,形狀必須相同,每個公司的土地必須連成一片。要求求出合理的劃分方案,以及所有可能的劃分方案總數。

二、需求分析

由於題目要求土地只能沿網格線劃分,每個公司土地必須與總控站連通且連成一片,四塊圖地大小、形狀必須相同,每個公司土地恰好包含一個煤礦一個鐵礦,所以如果能求出一個公司的土地連在一塊,而且這個公司土地的形狀與其它三個公司的土地形狀一樣,並且每個公司都包含一個鐵礦和一個煤礦,即可得到一個合理的劃分方案。

由於中心的四個網格已經分給四個公司建立總控站,假設我們已知滿足題目要求的A公司的土地形狀,那麼把公司A的土地形狀分別旋轉90°,180°,270°即可得到公司B,D,C的土地形狀,並且每個公司都包含一個鐵礦和一個煤礦,那麼這樣得到的劃分方案肯定是一種解。因此我們就只需搜尋A公司的土地形狀,其他公司的土地形狀就可以推出來,這樣複雜度就大大的降了下來。

 

三、演算法與資料結構設計

3.1演算法思想分析

(1)假設根據A公司的土地形狀可通過旋轉90°,180°,270°得到公司B,D,C的土地形狀,那麼我們需要先求得A公司土地形狀。

(2)由於要將6*6的網格的土地平分成四塊給這四個公司管理,所以每個公司佔有九個網格,因此A公司的土地形狀是與公司A總控站相連的九個網格。由於每個公司都要有一個煤礦和一個鐵礦,所以公司A土地的生成是要受其他公司影響的,在給A公司分配土地時也要判斷該土地旋轉90°,180°,270°後與B,D,C三個公司對應的三個土地分配給B,C,D三個公司後是否滿足分配條件。

(3)公司擴張土地的約束條件:

①由於每個公司的土地都必須連成一片,假設公司A目前已確定k-1個連通網格作為其屬地,接下來公司A的第K個網格必須在公司A所屬的網格1,2…K-1的四個相鄰的方向上選擇。

②判斷一個網格能否劃入公司A的土地:

由於一個網格僅僅能被一個公司佔有,因此要求該網格不被任一公司佔有;

由於每個公司僅開發一個煤礦和一個鐵礦,如果把這塊土地以及與它成中心對稱的3塊土地分別劃分給公司A,B ,C,D,如果發現某個公司已經擁有的煤礦數或鐵礦數大於1,或擁有的空地數大於7,那麼這個土地就不能劃分給公司A。

③由於把一個解旋轉所得到得一組解都視為同解,這樣的話就會得到重複的解。為了防止產生重複的解,使效率可以大大的提高,我們在判斷某個網格能否劃入公司A的時候,要求該網格的四個相鄰要麼在界外,要麼不在公司A的第1至K-1個網格的重合。這樣就可以無重複的搜尋到所有的解。

 

3.2 資料結構

(1)解陣列comp。comp[x][y]表示座標為(x,y)的網格屬於哪個公司。

comp[x][y]=0,表示網格(x,y)未被佔領;

comp[x][y]=1,表示網格(x,y)屬於A公司;

comp[x][y]=2,表示網格(x,y)屬於B公司;

comp[x][y]=3,表示網格(x,y)屬於C公司;

comp[x][y]=4,表示網格(x,y)屬於D公司。

(2)土地型別陣列map,表示(x,y)網格所代表的土地型別。

map[x][y]=0,表示網格(x,y)的土地無礦;

map[x][y]=1,表示網格(x,y)的土地是煤礦;

map[x][y]=2,表示網格(x,y)的土地是鐵礦。

(3)資源記錄陣列note。note陣列記錄各公司的資源分配情況。

note[comp[x][y]][ map[x][y]]

表示comp[x][y]公司擁有的map[x][y]型別的土地個數。

(4)記錄座標陣列list。List是記錄A公司網格的座標的陣列,記錄每個網格位置。

    list[1][0] = 2;//表示A公司第一個網格的列座標是2

    list[1][1] = 2;// 表示A公司第一個網格的行座標是2

(5)A公司網格陣列tab。Tab陣列記錄座標(x,y)放置的是A公司的第幾個網格。

tab[2][2] = 1;表示行座標為2,列座標為2的網格放置的是A公司的第一個網格

 

3.3 演算法流程

1,初始化,把各個公司的總控站分給各公司。

2.  繼續為公司A分配符合約束條件的網格。

為公司A分配第deep個網格的過程:

由於每個公司的土地都必須連成一片,在搜尋第deep塊網格時,應從公司A所屬的網格1,2…deep-1的網格的四個相鄰的方向上選擇,假設現在正在擴充套件第K塊的四個方向。

int dirx[] = { 0, 1, 0, 0, -1};// 列座標增量

int diry[] = { 0, 0, 1, -1, 0};// 行座標增量

        if (k > deep - 1)// 由於各塊相連,所以第deep塊是之前的某塊的擴充套件

               return;

        if (deep > 9) {

           輸出一個解

           return;

        }

        for (i = 1; i <= 4; i++) {

//求出第deep塊網格的座標

           list[deep][0] = list[k][0] + dirx[i];

           list[deep][1] = list[k][1] + diry[i];

           if符合分配條件{

               確定第deep塊的位置;

               分配給A,B,C,D公司相應的土地塊;

               search(deep + 1, k);// 繼續查詢下一塊

               釋放第deep塊的位置;

               返回到上一節點

           }

        }

        // 將第deep點的座標清空

        list[deep][0] = 0;

        list[deep][1] = 0;

        search(deep, k + 1); // 擴充套件第K+1塊

    end

 

3.4 模組劃分

資源分配問題主要分為四個模組,分別是為A,B,C,D四個公司分配資源,判斷網格(x,y)是否符合分配的約束條件,返回上一節點狀態以及遞迴搜尋符合要求的解。

為A,B,C,D公司分配資源的虛擬碼

assign( x, y) {

根據A公司網格(x,y)分別求出對應的B,D,C網格位置;

將對應位置分配給相應的公司,comp[x][y] = 1-4;

將各位置對應的土地資源型別與對應公司記入note陣列+1,記錄各公司的資源分配情況

}

返回上一節點狀態的虛擬碼

revert( x, y) {

根據A公司網格(x,y)分別求出對應的B,D,C網格位置;

將各位置對應的各公司的資源分配情況-1,返回未分配前的資源狀態;

將對應位置的分配狀態置零,返回未分配的狀態,comp[x][y] = 0。

}

判斷網格是否符合分配的約束條件的虛擬碼

Boolean 可擴充套件{

If 網格(x,y)已被佔有或不在網格範圍內

Return false;

If 把這塊土地以及與它成中心對稱的3塊土地分別劃分給公司A,B ,C,D後,

發現某個公司已經擁有的煤礦數或鐵礦數大於1,或擁有的空地數大於7

Return false;

If  (x,y)的四個相鄰在界內並且與公司A的第1至K-1個網格的重合(重複解)

Return false;

Return true;

    }

遞迴搜尋符合要求的解的虛擬碼

Search(deep,k){

// 搜尋第deep塊,擴充套件第K塊的四個方向

求出第K塊的四個方向的位置座標,

判斷各位置座標是否符合分配的約束條件,

If(符合約束條件){

確定第deep塊的位置;

Assign(list[deep][0],list[deep][1]);//分配給A,B,C,D公司相應的土地塊

Search(deep+1,k);

清空第deep塊的位置;

Revert(list[deep][0],list[deep][1]);//返回上一節點狀態

}

Search(deep,k+1);//繼續搜尋第K+1的四個方向

}

 

各個模組之間的呼叫關係如下圖所示:

 

3.5 核心演算法的時間複雜度分析

在採用回溯法對A公司的其他網格進行搜尋的過程中,

最壞情況下:T(deep,k)= 4T(deep+1,k)+ T(deep,k+1)

最好情況下:T(deep,k)= T(deep+1,k)

四、程式設計實現與程式測試

4.1 核心程式碼

    // 判斷某個網格是否能擴充套件

    public static boolean check(int x, int y,int deep, int[][] comp, int[][] tab, int[][] note, int[][] map) {

        int i, temp, Rx, Ry;

// 網格(x,y)已被佔有或不在網格範圍內

        if (!inside(x, y) || comp[x][y] > 0){

           return false;

        }

        // 要求(x,y)的四個相鄰要麼在界外或不在公司A的第1至K-1個網格的重合。這樣就可以無重複的搜尋到所有的解。

        for (i = 1; i <= 4; i++) {

           Rx = x + dirx[i];

           Ry = y + diry[i];

           if (inside(Rx, Ry) &&tab[Rx][Ry] > 0 && tab[Rx][Ry] < deep)

               return false;

        }

        /*

         *把這塊土地以及與它成中心對稱的3塊土地分別劃分給公司A,B ,C,D。判斷髮現某個公司已經擁有的煤礦數或鐵礦數大於1,或擁有的空地數大於7

         */

        Rx = x;

        Ry = y;

        for (i = 0; i < 4; i++) {

           int s = c[i];// 公司

           int j = map[Rx][Ry];// 土地性質

// 已有煤礦/鐵礦,新加入的土地為煤礦/鐵礦

           if ((map[Rx][Ry] > 0) &&(note[s][j] > 0))

               return false;

// 已有六塊空地,新加入的仍為空地

           if ((map[Rx][Ry] == 0) &&(note[s][j] == 7))

               return false;

           else {

//迴圈獲取對應的B,D,C公司土地塊座標

               temp = Rx;

               Rx = Ry;

               Ry = 5 - temp;

           }

        }

        return true;

    }

    // 遞迴搜尋

    public static void search(int deep, int k,int[][] comp, char[] COMP, int[][] list, int[][] tab,

           int[][] note, int[][] map) {// 搜尋第deep塊,擴充套件第K塊的四個方向

        int i = 0;

        if (k > deep - 1)// 由於各塊相連,所以第deep塊是之前的某塊的擴充套件

               return;

        if (deep > 9) {

           count++;// 解的個數

           print(comp, COMP, note);// A公司有9塊網格

           return;

        }

        for (i = 1; i <= 4; i++) {

           // int flag;

           list[deep][0] = list[k][0] + dirx[i];

           list[deep][1] = list[k][1] + diry[i];

           if (check(list[deep][0],list[deep][1], k, comp, tab, note, map)) {// 檢查是否符合分配條件

               tab[list[deep][0]][list[deep][1]]= deep;// 確定第deep塊

               assign(list[deep][0],list[deep][1], comp, note, map);// 分配給A,B,C,D公司相應的土地塊

               search(deep + 1, k,  comp, COMP, list, tab, note, map);// 繼續查詢下一塊

               tab[list[deep][0]][list[deep][1]]= 0;

               revert(list[deep][0],list[deep][1], comp, note, map);// 回溯

            }

        }

        // 將第deep點的座標清空

        list[deep][0] = 0;

        list[deep][1] = 0;

        search(deep, k + 1,  comp, COMP, list, tab, note, map); // 擴充套件第K+1塊

    }

參考: GodofTheFallen. 平分資源Resource  http://blog.csdn.net/qq_22141519/article/details/47168935