最近找实习,开始练习一些简单的ACM题,感叹自己弱爆了啊……

POJ2227大致题意:有一个W*H的方形格子,每个1*1的单位格子上有一个实心柱子,柱子高高低低,中间有地落差可以注入Juice,给定W*H上柱子的高度数组,求可以注入的Juice高度。这道题我读了很久才懂,关键是柱子是实心的……哎,这语文水平。

输入例子:

4 5
5 8 7 7
5 2 1 5
7 1 7 1
8 9 6 9
9 8 9 9

主要思想:DFS+优先队列(堆),首先,周围的柱子上显然不能注水,不然Juice会白白流走。于是,将一周的柱子进入队列(这里可以是一般的队列,只考虑DFS),从队列中取出一个元素,看它四周(上下左右四个方向)的柱子,若有高度大于该元素的(注意:从队列中出来的元素说明是不能装水的,换言之,是“容器”的边界)的,将该柱子入队(也即列入边界行列),既然引出了边界的概念,那么可以自然地联想到木桶原理,应该将边界中最短的那根柱子作为优先考虑,所以我们要用到优先队列。若队列中的元素四周有高度小于它的柱子(隐含的表明该柱子的高度小于所有边界的高度),则该柱子与该元素的高度差是可以注入的Juice的量,处理好该柱子后,将其高度更新到元素的高度(可以想象注水的过程),最后,将该柱子入队。

显示源码
  1 #include <stdio.h>
2
3 #include <iostream>
4 #include <vector>
5 #include <queue>
6
7 using namespace std;
8
9 const int MAX = 301;
10
11 //Linux 平台下64位的整数为long long类型,输入用scanf("%lld", &a)
12 //Windows的VC下为__int64,输入用scanf("%I64d", &a)
13 typedef long long LongInt;
14
15 struct Node{
16 int x, y;
17 LongInt height;
18 };
19
20 struct cmp{
21 bool operator()(const Node& first, const Node& second){
22 return first.height > second.height;
23 }
24 };
25
26 //全局变量
27 bool state[MAX][MAX];
28 int W, H;
29 int move[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; //控制上下左右四个方向的横纵坐标
30 LongInt result = 0, bowl[MAX][MAX];
31 priority_queue<Node, vector<Node>, cmp> border;
32
33 void Input(void){
34 scanf("%d %d", &W, &H);
35 for(int i = 1;i <= H;++ i){
36 for(int j = 1;j <= W;++ j){
37 scanf("%lld", &bowl[i][j]);
38 state[i][j] = false;
39 }
40 }
41 }
42
43 void Init(void){
44 //将周围一圈都push到优先队列中
45 for(int i = 1;i <= W;++ i){
46 //记录状态矩阵state
47 state[1][i] = true;
48 state[H][i] = true;
49
50 Node first_node, last_node;
51 first_node.x = 1;
52 first_node.y = i;
53 first_node.height = bowl[1][i];
54 last_node.x = H;
55 last_node.y = i;
56 last_node.height = bowl[H][i];
57 border.push(first_node);
58 border.push(last_node);
59 }
60 for(int i = 1;i <= H;++ i){
61 state[i][1] = true;
62 state[i][W] = true;
63
64 Node first_node, last_node;
65 first_node.x = i;
66 first_node.y = 1;
67 first_node.height = bowl[i][1];
68 last_node.x = i;
69 last_node.y = W;
70 last_node.height = bowl[i][W];
71 border.push(first_node);
72 border.push(last_node);
73 }
74 }
75
76 void FloodFill(int row, int col, LongInt height){
77 for(int i =0; i < 4; ++ i){
78 int x = row + move[i][0];
79 int y = col + move[i][1];
80
81 //越过边界或者该节点已经访问过, pass
82 if(x <= 0 || x > H || y <= 0 || y > W ||
83 state[x][y]){
84 continue;
85 }
86
87 LongInt h = bowl[x][y];
88 //设置该节点已访问
89 state[x][y] = true;
90 Node current;
91 current.x = x;
92 current.y = y;
93 current.height = h;
94 //若周围节点的高度小于当前节点的高度,则可以装水,更新高度后push入优先队列
95 if(h < height){
96 int diff = height - h;
97 //printf("%d, %d, %d\n", x, y, diff);
98 result += diff;
99 bowl[x][y] = height;
100 current.height = height;
101 }
102 border.push(current);
103 }
104 }
105
106 void Solve(void){
107 while(! border.empty()){
108 Node front_item = border.top();
109 FloodFill(front_item.x, front_item.y,
110 bowl[front_item.x][front_item.y]);
111 //printf("Position:%d, %d\n", front_item.x, front_item.y);
112 border.pop();
113 }
114 }
115
116 int main(int argc, char *argv[]){
117 Input();
118 Init();
119 Solve();
120 printf("%lld\n", result);
121 }

写代码的过程中遇到的问题:

首先,高度的范围是 (1 <= B <= 1,000,000,000),需要用到long long类型(注:我的编译环境是g++,若是windows下用类型__int64)。

第二,考虑二维矩阵中某个元素的四个方向,可以用一个二维记录横纵坐标的偏移值,如int move[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}中{1,0}表明向右时横坐标的位移是1,纵坐标位移是0,这样有利于代码的整洁和清晰。

最后,在写函数对象类cmp时,在重载函数调用符()时,用的是>,才能使优先队列的堆顶是最小值(也即是最小堆),这个原理需要看清楚。