你的任务是模拟洪水灾害。
对于给定的横截面图,给出淹没部分的面积。
假设该地区不断地下雨,而从该地区溢出的水正在落入两侧的海中。 例如,对于上面的横截面图,雨水将产生洪水,其面积分别为 4、2、1、19 和 9。
输入
在一行中给出一个字符串,分别用"/“,”“和”_"表示斜坡和平原。 例如,上面例子的区域由字符串“\ \ / / / \ _ / \ / \ \ \ \ / _ / \ \ / / / _ _ \ \ \ _ \ \ / _ \ / _ / ”给出。
输出
按以下格式输出:
A
K L1,L2,…,Lk
在第一行,打印出洪水区域的总面积A。
在第二行,打印出洪水区域的个数K和每个区域的面积Li(从左到右)。
约束
1 ≤ length of the string ≤ 20000
看到这道题,我首先想到我们可以从头开始,一格一格向后找到开始往下凹的地方,持续跟踪直到高度与之前持平。然后继续往后直到又开始下降然后重复之前的过程。。
然后我发现,很容易就会遇到,再也达不到之前开始下降的高度 的情况,于是我果断地放弃这种方案,写了一个大模拟:
直接构建一个横截面图,用0代表空气,1代表山体,2代表整格水,3代表下降的半格水,4代表上升的半格水
先把山以上的部位全部填满水,然后重复执行:将左右下方任一方向是空气的水扔掉,直到再也扔不掉为止,剩下的就是我们的积水
最后用dfs找到每一坨水,算出面积
然后我们发现地图是个二维数组,定义成ditu[MAXN][MAXN]
的话会直接爆空间。然后我们考虑到应该不会有一直下降或上升的山所以高度用不到那么多格,于是尝试开小第二维。
喜提两个TLE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 #include <stdio.h> #define MAXN 20005 #define INF ((int)0x3f3f3f3f) #define min(a, b) ((a) > (b) ? (b) : (a)) #define max(a, b) ((a) < (b) ? (b) : (a)) int a[MAXN] = {0 };int ditu[MAXN][1005 ] = {0 };short vis[MAXN][1005 ] = {0 };int mmjimf[MAXN] = {0 };int newm = 0 ;double dfs (int x, int y) { if (vis[x][y]) { return 0. ; } vis[x][y] = 1 ; double value = 0 ; switch (ditu[x][y]) { case 2 : value = 1 ; break ; case 3 : case 4 : value = 0.5 ; break ; default : return 0 ; } if (ditu[x][y] == 2 ) value += dfs(x + 1 , y) + dfs(x - 1 , y) + dfs(x, y + 1 ) + dfs(x, y - 1 ); else if (ditu[x][y] == 3 ) { value += dfs(x + 1 , y) + dfs(x, y + 1 ) + dfs(x, y - 1 ); if (ditu[x - 1 ][y] != 4 ) { value += dfs(x - 1 , y); } } else if (ditu[x][y] == 4 ) { value += dfs(x - 1 , y) + dfs(x, y + 1 ) + dfs(x, y - 1 ); if (ditu[x + 1 ][y] != 3 ) { value += dfs(x + 1 , y); } } return value; } int main (void ) { char c = getchar(); int newp = 1 ; int zvdi = INF, zvgc = -INF; while (c == '/' || c == '\\' || c == '_' ) { ++newp; switch (c) { case '/' : a[newp] = a[newp - 1 ] + 1 ; break ; case '\\' : a[newp] = a[newp - 1 ] - 1 ; break ; case '_' : a[newp] = a[newp - 1 ]; break ; } if (a[newp] < zvdi) { zvdi = a[newp]; } c = getchar(); } for (int i = 1 ; i <= newp; ++i) { a[i] += (-zvdi + 1 ); if (a[i] > zvgc) { zvgc = a[i]; } } for (int i = 1 ; i < newp; ++i) { int low = min(a[i], a[i + 1 ]); int high = max(a[i], a[i + 1 ]); for (int j = 1 ; j <= zvgc; ++j) { if (j <= low) { ditu[i][j] = 1 ; } else if (j >= low && j <= high) { if (low != high) { if (a[i] > a[i + 1 ]) ditu[i][j] = 3 ; else ditu[i][j] = 4 ; } else { ditu[i][j] = 2 ; } } else if (j > high) { ditu[i][j] = 2 ; } } } int vcdc = 0 ; do { vcdc = 0 ; for (int j = zvgc; j >= 1 ; --j) { for (int i = 1 ; i < newp; ++i) { if (ditu[i][j] == 2 || ditu[i][j] == 3 || ditu[i][j] == 4 ) { if (!((ditu[i - 1 ][j] || ditu[i][j] == 3 ) && (ditu[i + 1 ][j] || ditu[i][j] == 4 ) && ditu[i][j - 1 ])) { ++vcdc; ditu[i][j] = 0 ; } } } } } while (vcdc); double mmji = 0 ; for (int j = zvgc; j >= 1 ; --j) { for (int i = 1 ; i < newp; ++i) { switch (ditu[i][j]) { case 2 : mmji += 1 ; break ; case 3 : case 4 : mmji += 0.5 ; break ; } } } printf ("%.0lf\n" , mmji); for (int i = 1 ; i < newp; ++i) { for (int j = zvgc; j >= 1 ; --j) { if (!vis[i][j]) { double ans = dfs(i, j); if (ans) mmjimf[++newm] = ans; } } } printf ("%d " , newm); for (int i = 1 ; i <= newm; ++i) { printf ("%d " , mmjimf[i]); } return 0 ; }
遇到严重超时TLE,我内心十分悲痛,打开了必应,搜索全网,搜到了一个题解。这个题解告诉我们
由淹没区域可以想到 ‘\’ 与 ‘/’ 的配对。
此题比较特别的是如何组成一整个被水淹没的区间?答案是将所有配对的区间中 相互包含的区间合并。
如何合并呢?可以知道大区间所包含的小区间必定比大区间先入栈,则可以依此合并。
如何计算横截面积?所有配对的‘\’与’/'之间形成的横截面都为梯形或三角形,把问题分解为多个小问题来解答。
我看完大受启发,决定按此思路写一写。写了一段后发现,这哪里用得到栈,好像只需要加加减减就可以了。
这时我发现,只要我把最开始的做法变得更暴力一点,从每一个起点出发,如果再也达不到之前开始下降的高度,就扔掉这个起点,换下一个起点。这样做不仅不会浪费太多空间,而且比大模拟快很多,顺利AC。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 #include <stdio.h> #define MAXN 20005 char sgn[MAXN] = {0 };double ans[MAXN] = {0 };double tot = 0 ;int news = 0 ;int newa = 0 ;int main () { int h = 0 ; char t = getchar(); while (t == '/' || t == '\\' || t == '_' ) { sgn[++news] = t; t = getchar(); } int i = 1 ; while (i <= news) { double sum = .5 , hi = 1 ; if (sgn[i] != '\\' ) { ++i; continue ; } int st = 1 ; int j; for (j = i + 1 ; j <= news && st > 0 ; ++j) { if (sgn[j] == '\\' ) { ++st; ++hi; sum += (hi - .5 ); } else if (sgn[j] == '/' ) { --st; --hi; sum += (hi + .5 ); } else { sum += hi; } } if (st == 0 ) { i = j; ans[++newa] = sum; tot += sum; } else { ++i; } } printf ("%.0lf\n%d " , tot, newa); for (int i = 1 ; i <= newa; ++i) { printf ("%.0lf " , ans[i]); } return 0 ; }
复健之路进行中~~