资源分配问题

一、问题描述

已知一块土地被划分成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公司土地形状。

扫描二维码关注公众号,回复: 4776931 查看本文章

(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


猜你喜欢

转载自blog.csdn.net/TangxinMantou10503/article/details/80212739