题目大意:在一个1000000*1000000的矩阵中放入几块木板,问你这些木板把矩阵划分成了几个区域?输入会给左下角和右上角的坐标,输入W==0且H==0结束。
这一题是书上的作业题,书上有一道差不多的例题,但是书上那道例题是用的直线的,而且他的坐标是点格,而这道题是坐标(这个很重要,我一开始没有区分好导致理解不了)。那么这一题肯定要用到坐标压缩的(1000000*1000000太大了,我们可以把木板压缩到最小就可以了),标准的直接看代码就好了,很容易理解。)
然后现在这题很难的一个地方在于,怎么高效地判断和填充木板区域,当然我们可以一个一个木板找,然后匹配填充,但是显然这样比较慢(这种操作复杂度是0(W*H*N),虽然坐标压缩了以后这个不会很大,但是还是有其他更好的方法的),这里有一个很神奇的imos法,imos法不仅仅是用来做这种ACM的题的,还是一个很实用的工程算法。
imos法详解(中文版,我暂时还没得到原作者的同意,所以直接丢博主的链接好了)
http://www.hankcs.com/program/algorithm/imos_method.html
imos法详解(日文原版,这里有上面中文版没有的imos如何应对特殊的三角形矩阵的填充和一些函数(二次函数,高斯函数)的问题)
http://imoz.jp/algorithms/imos_method.html
(ps:上面两个的那个影响力计算的图是错的(代码没有错),-1的位置错了。)
最后我们用BFS就可以了(DFS容易爆栈,当然你用栈来模拟我没话说)。
1 #include <iostream> 2 #include <algorithm> 3 #include <functional> 4 #include <string.h> 5 #include <stdio.h> 6 #include <vector> 7 #include <queue> 8 #define MAX_N 1010 9 10 using namespace std; 11 12 static int X1[1010], Y1[1010], X2[1010], Y2[1010], fld[MAX_N * 2][MAX_N * 2], 13 dx[4] = { -1, 0, 0, 1 }, dy[4] = { 0, -1, 1, 0 }; 14 15 static int compress(int *const, int *const, const int, const int); 16 static int bfs(const int, const int); 17 void imos(const int, const int, const int); 18 19 int main(void) 20 { 21 int W, H, N; 22 23 //freopen("D:\input.txt", "r", stdin); 24 while (1) 25 { 26 scanf("%d%d", &W, &H); 27 if (W == 0 && H == 0) 28 break; 29 scanf("%d", &N); 30 for (int i = 0; i < N; i++) 31 { 32 scanf("%d%d%d%d", &X1[i], &Y1[i], &X2[i], &Y2[i]); 33 } 34 W = compress(X1, X2, W, N); 35 H = compress(Y1, Y2, H, N); 36 37 imos(W, H, N); 38 cout << bfs(W, H) << endl; 39 } 40 return EXIT_SUCCESS; 41 } 42 43 static int compress(int *const s1, int *const s2, const int W, const int N) 44 { 45 //坐标离散化 46 vector<int>xs; 47 48 for (int i = 0; i < N; i++) 49 { 50 if (0 < s1[i] && s1[i] < W) xs.push_back(s1[i]); 51 if (0 < s2[i] && s2[i] < W) xs.push_back(s2[i]); 52 } 53 xs.push_back(0); 54 xs.push_back(W);//加上边界条件 55 sort(xs.begin(), xs.end()); 56 xs.erase(unique(xs.begin(), xs.end()), xs.end()); 57 58 for (int i = 0; i < N; i++) 59 { 60 s1[i] = find(xs.begin(), xs.end(), s1[i]) - xs.begin(); 61 s2[i] = find(xs.begin(), xs.end(), s2[i]) - xs.begin(); 62 } 63 return xs.size() - 1;//注意这里要获取的边界条件使得size加了2,要减1才能刚好变成真正的数组长度 64 } 65 66 static int bfs(const int W, const int H) 67 { 68 int ans = 0; 69 for (int i = 0; i < W; i++) 70 for (int j = 0; j < H; j++) 71 { 72 if (fld[i][j])continue;//搜索没有挡板的位置 73 ans++; 74 75 queue<pair<int, int> > que; 76 que.push(make_pair(i, j)); 77 while (!que.empty()) 78 { 79 int tx = que.front().first, ty = que.front().second; 80 que.pop(); 81 for (int i = 0; i < 4; i++) 82 { 83 int ttx = tx + dx[i], tty = ty + dy[i]; 84 if (0 <= ttx && ttx <= W 85 && 0 <= tty && tty <= H 86 && !fld[ttx][tty]) 87 { 88 que.push(make_pair(ttx, tty)); 89 fld[ttx][tty] = 1; 90 } 91 } 92 } 93 } 94 return ans; 95 } 96 97 void imos(const int W, const int H, const int N) 98 { 99 //imos法统计区间 100 memset(fld, 0, sizeof(fld)); 101 102 for (int i = 0; i < N; i++)//统计影响力 103 { 104 fld[X1[i]][Y1[i]]++; 105 fld[X1[i]][Y2[i]]--; 106 fld[X2[i]][Y1[i]]--; 107 fld[X2[i]][Y2[i]]++; 108 } 109 for (int i = 0; i < W; i++)//累计横向 110 for (int j = 1; j < H; j++) 111 fld[i][j] += fld[i][j - 1]; 112 113 for (int j = 0; j < H; j++)//累计纵向 114 for (int i = 1; i < W; i++) 115 fld[i][j] += fld[i - 1][j]; 116 //非零部分就是有挡板的位置了 117 }
另外吐槽一下AOJ,编译器是个什么鬼编译器,连queue<pair<int,int>>都要报错,居然不能识别pair<int,int>和queue的>的区分,不过AOJ有input和output文件,这个好评
参考:http://www.hankcs.com/program/algorithm/aoj-0531-paint-color.html