某个矿藏丰富的地区埋有不同价值的矿藏。Mr. Chen某日记起他在此处有一块领地,于是他很想知道自己的领地上到底包含价值多少的矿物。他在该地区的地图上画出了领地的边界,请你来为他做一个资产评估。
假定整个地区的地图为矩形。为了方便起见,我们把这个地区划分成若干单位正方形区域,每个区域以该处的矿藏价值标识。Mr. Chen划出的边界是一条封闭的曲线且不自交。边界经过的单位区域由于墨水的缘故,上面的矿藏价值标识变得不可辨认,保守估计,可认为价值为0。
输入:第一行为两个整数N, M (1<= N<=50,1<=M<=50),表示地图有N行M列单位区域构成。接下来的N行为地图。每行有M个单位区域的标识,每个标识或者为一非负整数,表示该处矿藏价值;或者为‘*’,表示领地的边界经过这一单位区域。每两个单位区域的表示之间以至少一个空格隔开。
输出:一个整数,即领地范围内所有矿藏的价值之和。
输入样例
5 6
231 11 41 5 7 1
13 * * * 31 34
* 81 65 23 * 2
1 * * * 7 1
20 4 2 89 6 3
输出样例
169
1、作图G=(V,E)。所有非边界的单位区域对应顶点集V中的一个顶点,V={vij | (i,j)非边界区域}。若两个非边界的单位区域相邻,则相应顶点之间有无向边。
2、扩充图G:先扩充V,在原有地图的外围加一圈顶点:v00, v01, …, v0,M+1,vN+1,0, vN+1,1, …, vN+1,M+1,v10, v20, …, vN0,v1,M+1, v2,M+1, …, vN,M+1。新顶点的矿藏价值为0。再相应扩充E,所有原来地图边缘顶点与这些新顶点连边。
设整个地图的矿藏总值为A,Mr.Chen领地(不含v00)中的矿藏价值为AS,剩下部分的矿藏价值为AT,则AS+AT=A。求S有两条途径:
(a) 遍历不含v00的子图S,直接统计AS;
(b) 遍历含v00的子图T,统计AT,由AS=A-AT求出AS,其中A可通过两重循环简单求得。
如果用(a)计划,必须需要先找到S中的一个顶点,然后开始遍历,要做到这一点比较困难。相比之下,如果用(b)计划,可直接从v00开始遍历,省去不少麻烦,所以我们用(b)计划。
种子染色法可以形象的理解为在图中抛下一颗种子(顶点v00),一种颜色就朝着四面八方蔓延开来,填满所有可达区域。我们用队列q存储非Mr.Chen领地中待扩展单位区域的坐标,按照“先进先出”的顺序扩展这些单位区域,直至队列q空为止。此时,非Mr.Chen领地的区域全部被染色,矿藏总值A减去被染色区域内的矿藏价值即为问题的解。
设地图的行数为N,列数为M,地图为a,其中单位区域(r,c) 中的矿藏价值为a[r,c]。如果 (r,c)为边界,则a[r,c]=‘*’。写程序时也你可以用某个特殊的数代替‘*’,例如-1。
1 Readln(n,m); /*输入地图信息*/ 2 for c←1 to n do 3 for r←1 to m do read(read(a[c,r]); 4 /*设边缘顶点的矿藏价值为0*/ 5 for c←0 to M+1 do{a[0,c]←0;a[N+1,c]←0 }; 6 for r←1 to N do {a[r,0]←0;a[r,M+1]←0}; 7 A←0; /*累计个地图的矿藏总值*/ 8 for r←1 to N do 9 for c←1 to M do if a[r,c] ‘*’ then A←A+a[r,c]; 10 (0,0)进入队列q; 11 while q队列非空do 12 { 取出q的队首坐标(r,c); 13 SUB-Test(r-1,c);SUB-Test(r+1,c);/*依次处理(r,c)的4个相邻位置*/ 14 SUB-Test(r,c-1);SUB-Test(r,c+1); 15 q出队操作 };/*while*/ 16 writeln(A); 17 其中过程SUB-Test(r,c)判断区域(r,c)是否在地图外或为边界。如果不是,则入队,并从A中扣去该区域矿藏价值,同时置已访问标志”*”。 18 Proc SUB-Test(r,c:integer); 19 { if(r0)and(c0)and(rN+1)and(cM+1)and(a[r,c]‘*’) 20 then{ (r,c)进入队列q;A←A-a[r,c];a[r,c]←‘*’}/*then*/ 21 }/* SUB-Test */