Greedy:Paint Color(AOJ 0531)

IMG_4037

涂颜料

  题目大意:在一个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

原文地址:https://www.cnblogs.com/Philip-Tell-Truth/p/5195984.html