Problem:
ペンキの色

問題

情報オリンピックの宣伝のために,長方形のベニヤ板にペンキを塗り看板を制作したい.ベニヤ板には色を塗りたくないところにあらかじめ何枚かの長方形のマスキングテープが貼られている.そこでマスキングテープで区切られた領域ごとに別々の色を使いペンキを塗ることにした.例えば,図 5-1 の場合は 5 色のペンキを使う.

入力としてマスキングテープを貼る位置が与えられた時,使うペンキの色の数を求めるプログラムを作成せよ.ただし,ベニヤ板全体がマスキングテープで覆われることはなく,全てのマスキングテープの辺はベニヤ板のいずれかの辺に平行である.

入力

入力は複数のデータセットからなる．各データセットは以下の形式で与えられる．

1 行目にはベニヤ板の幅 w (1 ≤ w ≤ 1000000 となる整数) と高さ h (1 ≤ h ≤ 1000000 となる整数) がこの順に空白区切りで書かれている.

2 行目にはマスキングテープの数 n (1 ≤ n ≤ 1000 となる整数) が書かれている.
続く 3 行目以降の 2 + i 行目 (1 ≤ i ≤ n) には,i 番目に貼るマスキングテープの左下の座標 (x1 , y1 ) と,右上の座標 (x2 , y2 ) が x1 , y1 , x2 , y2 (0 ≤ x1 < x2 ≤ w, 0 ≤ y1 < y2 ≤ h となる整数) の順に空白区切りで書かれている.

ただし,ベニヤ板の左下の角の座標は (0, 0) で右上の角の座標は (w, h) である. 採点用データのうち, 配点の 30% 分は w ≤ 100, h ≤ 100, n ≤ 100 を満たす.

h, w がともに 0 のとき入力の終了を示す. データセットの数は 20 を超えない．

出力

データセットごとに使うペンキの色数を１行に出力する.

入出力例

次の例は図 5-1 の場合である.

入力例

15 6
10
1 4 5 6
2 1 4 5
1 0 5 1
6 1 7 5
7 5 9 6
7 0 9 2
9 1 10 5
11 0 14 1
12 1 13 5
11 5 14 6
0 0

出力例

5

上記問題文と自動審判に使われるデータは、情報オリンピック日本委員会が作成し公開している問題文と採点用テストデータです。

Solution in C++ (judge verdict: Accepted)

#include <fstream>
#include <iostream>
#include <vector>
#include <map>
#include <queue>
#include <utility>
#include <algorithm>
using namespace std;
int n;
int dx[4] = {1,0,-1,0};
int dy[4] = {0,1,0,-1};

// 座標は 2n+2 個ある
void coordinate_compress(vector<int> &xl, vector<int> &xr, int &t) {
  vector<int> xc(2 * n + 2);
  for (int i = 0; i < n; i++) {
    xc[i]     = xl[i];
    xc[i + n] = xr[i];
  }
  xc[2 * n] = 0;
  xc[2 * n + 1] = t;

  sort(xc.begin(), xc.end());
  // 重複を削除
  xc.erase( unique(xc.begin(), xc.end()), xc.end() );

  map<int,int> zip;
  for (int i = 0; i < xc.size(); i++) {
    zip[xc[i]] = i;
  }

  for (int i = 0; i < n; i++) {
    xl[i] = zip[ xl[i] ];
    xr[i] = zip[ xr[i] ];
  }
  t = zip.size() - 1;
}

int main() {
  // ifstream cin("../test.txt");
  while (true) {
    int w,h;
    cin >> w >> h >> n;

    if (!(w|h)) break;

    vector<int> xl, yl, xr, yr;
    xl = vector<int>(n);
    xr = vector<int>(n);
    yl = vector<int>(n);
    yr = vector<int>(n);
    for (int i = 0; i < n; i++) {
      cin >> xl[i] >> yl[i] >> xr[i] >> yr[i];
    }

    // 座標圧縮
    coordinate_compress(xl, xr, w);
    coordinate_compress(yl, yr, h);


    // 表示　テープの座標
    // for (int i = 0; i < n; i++) {
    //   cout << w << " " << h << endl;
    //   cout << xl[i] << " " << yl[i] << " " << xr[i] << " " << yr[i] << endl;
    // }

    // テープを貼る
    // 格子は左下の座標を基準にする
    vector< vector<int> > m (w, vector<int>(h+1));
    for (int i = 0; i < w; i++) {
      for (int j = 0; j <= h; j++) {
        m[i][j] = 0;
      }
    }
    for (int i = 0; i < n; i++) {
      for (int x = xl[i]; x < xr[i]; x++) {
        m[x][yl[i]] += 1;
        m[x][yr[i]] -= 1;
        if (xl[i] >= w || xr[i] > w || yl[i] >= h || yr[i] > h)
          cout << "warning" << endl;
      }
    }

    for (int y = 1; y < h; y++) {
      for (int x = 0; x < w; x++) {
        m[x][y] += m[x][y-1];
      }
    }

    // 色を幅優先で塗る
    int cnt_clr = 0;
    queue< pair<int, int> > qu;
    for (int x = 0; x < w; x++) {
      for (int y = 0; y < h; y++) {
        if (m[x][y] <= 0) { // テープがなければ色塗り開始
          cnt_clr++;
          qu.push(make_pair(x,y));
          while (!qu.empty()) {
            pair<int, int> p = qu.front();
            qu.pop();
            for (int i = 0; i < 4; i++) {
              int nx = p.first  + dx[i];
              int ny = p.second + dy[i];
              if (nx >= 0 && nx < w && ny >= 0 && ny < h && m[nx][ny] <= 0) {
                m[nx][ny] = 1;
                qu.push(make_pair(nx,ny));
              }
            }
          }
        }
      }
    }

    // 出力
    cout << cnt_clr << endl;
  }
}