坐标离散化,imos
来源:互联网 发布:按键精灵文字输出源码 编辑:程序博客网 时间:2024/05/18 18:47
只是为了收藏一个很好的思路,算法。。。。
#include<alogrithm>#include<vector>#include<queue>#include<iostream>#include<cstring>#define MAX_N 1000+16using namespace std;int N,H,W;int x1[MAX_N],x2[MAX_N];,y1[MAX_N],y2[MAX_N];int fid[2*MAX_N][2*MAX_N];int dx[4]={1,-1,0,0},dy[4]={0,0,-1,1};int compress(int *x1,int *x2,int w){ vector<int>xs;for(i=0;i<N;i++){int tx1=x1[i],tx2=x2[i];if(1<=tx1&&tx1<W)xs.push_back(tx1);if(1<=tx2&&tx2<W)xs.push_back(tx2);}xs.push_back(0);xs.push_back(W);sort(xs.begin(),xs.end());xs.erase(unique(xs.begin(),xs.end()),xs.end());for(i=0;i<N;i++){x1[i]=find(xs.begin(),xs.end(),x1[i])-xs.begin();x2[i]=find(xs.begin(),xs.end(),x2[i])=xs.begin();}return xs.size()-1;}int bfs(){int ans=0;for(int i=0;i<H;i++){ for(int j=0;j<W;j++){if(fid[i][j])continue;ans++;queue<pair<int,int>>que;que.push(make_pair(j,i));while(!que.empty()){int nx=que.front().first,ny=que.front().second;que,pop();for(int i=0;i<4;i++){int tx=nx+d[i],ty=ny+dy[i];if(tx<0||W<tx||ty<0||H<ty||fid[ty][tx]>0)continue;que.push(make_pair(tx,ty));fid[tx][ty]=1;}}}}return ans;}int main(){while(scanf("%d%d",&W,&H)&&W&&H){ scanf("%d",&N); for(i=0;i<N;i++) {scanf("%d%d%d%d",&x1[i],x2[i],y1[i],y2[i]); } memset(fid,0,sizeof(fid)); W=compress(x1,x2,W); H=compress(y1,y2,H); for(i=0;i<N;i++) { fid[y1[i]][x1[i]]++; fid[y1[i]][x2[i]]--; fid[y2[i]][x1[i]]++; fid[y2[i]][x2[i]]--; } for(int i=0;i<H;i++) for(int j=1;j<W;j++) fid[i][j]+=fid[i][j-1];for(int i=1;i<H;i++)for(j=0;j<W;j++)fid[i][j]+=fid[i-1][j];printf("%d\n",bfs());}}
以下是别人的帖子
図 1: 1 次元上へ 0 次関数を加算するイメージ
for (int i= 0; i< T; i++) table[i]= 0;
- for (int i = 0; i < C; i++) {
- // 時刻 S[i] から E[i] - 1 までのそれぞれについてカウントを 1 増やす
- for (int j = S[i]; j < E[i]; j++) {
- table[j]++;
- }
- }
- // 最大値を調べる
- M = 0;
- for (int i = 0; i < T; i++) {
- if (M < table[i]) M = table[i];
- }
いもす法による解法
いもす法では,出入り口でのカウントのみをするという発想で入店時に +1 を,出店時に -1 を記録しておき,答えを求める直前に全体をシミュレートします.記録には O(C) が,シミュレートにはO(T) がかかるので,全体としての計算量はO(C+T) となります.
- for (int i = 0; i < T; i++) table[i] = 0;
- for (int i = 0; i < C; i++) {
- table[S[i]]++; // 入店処理: カウントを 1 増やす
- table[E[i]]--; // 出店処理: カウントを 1 減らす
- }
- // シミュレート
- for (int i = 0; i < T; i++) {
- if (0 < i) table[i] += table[i - 1];
- }
- // 最大値を調べる
- M = 0;
- for (int i = 0; i < T; i++) {
- if (M < table[i]) M = table[i];
- }
次元の拡張
いもす法の 1 番の特徴はナイーブな方法と比較して,次元の上昇に強いという点です.次元の呪いからは逃れられないので高次元に適用することは難しいですが,問題設定として 2 次元や 3 次元が用いられることは非常に多く,それらの状況においていもす法は有用です.問題例
あなたは様々な種類のモンスターを捕まえるゲームをしています.今あなたがいるのは W×H のタイルからなる草むらです.この草むらでは N 種類のモンスターが現れます.モンスターi はAi≦x<Bi, Ci≦y<Di の範囲にしか現れません.このゲームを効率的に進めるため,1 つのタイル上で現れるモンスターの種類の最大値が知りたいです.(ただし,W×H は計算可能な程度の大きさとし,N は大きくなりうるものとします.)図 2: モンスターが現れる矩形(赤色)と,
各タイルで現れるモンスターの種類の数
各タイルで現れるモンスターの種類の数
ナイーブな解法
ナイーブな解法としては,それぞれのモンスターについて現れる矩形の範囲に含まれるすべてのタイルについて 1 を足す方法が挙げられます.しかし,これの計算量は O(NWH) です.
- for (int y = 0; y < H; y++) {
- for (int x = 0; x < W; x++) {
- tiles[y][x] = 0;
- }
- }
- for (int i = 0; i < N; i++) {
- // モンスター i が現れる [(A[i],C[i]), (B[i],D[i])) の範囲に 1 を足す
- for (int y = C[i]; y < D[i]; y++) {
- for (int x = A[i]; x < B[i]; x++) {
- tiles[y][x]++;
- }
- }
- }
- // 最大値を調べる
- int tile_max = 0;
- for (int y = 0; y < H; y++) {
- for (int x = 0; x < W; x++) {
- if (tile_max < tiles[y][x]) {
- tile_max = tiles[y][x];
- }
- }
- }
いもす法による解法
いもす法では,矩形の左上 (A[i],C[i]) に+1 を,右上 (A[i],D[i]) に−1 を,左下(B[i],C[i]) に−1 を,右下(B[i],D[i]) に+1 を加算し,答えを求める直前に累積和を求めます.記録には O(N) が,累積和の計算にはO(WH) がかかるので,全体としての計算量はO(N+WH) となります.
- for (int y = 0; y < H; y++) {
- for (int x = 0; x < W; x++) {
- tiles[y][x] = 0;
- }
- }
- // 重みの加算 (図 3)
- for (int i = 0; i < N; i++) {
- tiles[C[i]][A[i]]++;
- tiles[C[i]][B[i]]--;
- tiles[D[i]][A[i]]--;
- tiles[D[i]][B[i]]++;
- }
- // 横方向への累積和 (図 4, 5)
- for (int y = 0; y < H; y++) {
- for (int x = 1; x < W; x++) {
- tiles[y][x] += tiles[y][x - 1];
- }
- }
- // 縦方向の累積和 (図 6, 7)
- for (int y = 1; y < H; y++) {
- for (int x = 0; x < W; x++) {
- tiles[y][x] += tiles[y - 1][x];
- }
- }
- // 最大値を調べる
- int tile_max = 0;
- for (int y = 0; y < H; y++) {
- for (int x = 0; x < W; x++) {
- if (tile_max < tiles[y][x]) {
- tile_max = tiles[y][x];
- }
- }
- }
図 3: 重みの加算の結果
図 4: 横方向への累積
図 5: 横方向への累積の結果
図 6: 縦方向への累積
図 7: 縦方向への累積の結果
特殊な座標系への拡張
いもす法は三角形や六角形の座標系にも適用できます.どこに重みを加算して,どのような方向に累積するのかは,目標となる形に対してできる限り数が少なくなるような方向に様々な方向に差分を取って見つけます.図 8: 三角形の座標系
差分を取る手法
図 9 は三角形の座標系において埋めるべき重みを表しています.緑の線は間が省略されていることを示します(一辺の長さが 4 である三角形の場合には線を無視してもこれらの図では同様の結果になります).いもす法でこのような重み付けを行うため,重みのあるセルの数が十分に少なくなるまで差分をとって,重みを付けるべきセルの配置と累積和をとる方向を知る必要があります. 図 9 〜 12 は差分をとる過程を表しています.これらの間に,「左上から右下への差分」「右上から左下への差分」「左から右への差分」をとったので,いもす法で三角形の累積和を求める場合は,図 12 のように数値を配置し逆の動作「左から右への累積和」「右上から左下への累積和」「左上から右下への累積和」を行います.図 9: 埋めるべき重み
図 10: 図 9 の左上から右下への差分
図 11: 図 10 の右上から左下への差分
図 12: 図 11 の左から右への差分
次数の拡張
競技プログラミングで出題される問題は高くとも 1 次までだとは思いますが,いもす法は高次の区分多項式関数も適用することができます.本来多項式で良い近似が得られないと思われている関数が区分多項式で表せられることがあり,それを利用すると連続ウェーブレット変換の高速化が可能になります.2 次関数の差分
試しに 1 次元上で 2 次関数のいもす法を行なってみましょう.012345678900014916253649
↓差分 ↑累積和
0123456789000135791113↓差分 ↑累積和
01234567890001222222↓差分 ↑累積和
01234567890001100000図 13: 2 次関数 (x−2)+2 の差分
ガウス関数といもす法
次数の拡張の例としてガウス関数を見てみましょう.いもす法を適用するため,式 1 の左辺に表されるようなガウス関数を,右辺のような区分多項式で近似します.この近似関数は図 14 に示されるように非常によく近似されています.式 1: ガウス関数(左辺)とその近似(右辺)
図 14: ガウス関数(青)と多項式近似(赤)
-12-11-10-9-8-7-6-5-4-3-2-10120033000000-11-11000
↓累積和 ↑差分
-12-11-10-9-8-7-6-5-4-3-2-10120036666666-5-16-16-16-16↓累積和 ↑差分
-12-11-10-9-8-7-6-5-4-3-2-1012003915212733394540248-8-24↓累積和 ↑差分
-12-11-10-9-8-7-6-5-4-3-2-101200312274875108147192232256264256232図 15: いもす法によって近似されたガウス関数
-12-11-10-9-8-7-6-5-4-3-2-10121
.493
.427
.2814
.4126
.5745
.5872
.76108
.08149
.41192
.20230
.09256
.31265
.70256
.31230
.09
.493
.427
.2814
.4126
.5745
.5872
.76108
.08149
.41192
.20230
.09256
.31265
.70256
.31230
.09
図 16: 近似目標となるガウス関数
0 0
- 坐标离散化,imos
- 坐标离散化技巧
- 坐标离散化处理
- 坐标离散化
- 坐标离散化
- 区域坐标离散化
- 坐标的离散化
- 坐标离散化
- GDUFS1162 Fences 坐标离散化
- poj 2528 坐标离散化+线段树
- 【坐标离散化】 挑战程序设计竞赛
- 区域的个数(坐标离散化)
- poj3109坐标离散化+树状数组
- 区域的个数 (坐标离散化)
- HDU4456 坐标转换+线段树+离散化
- Aizu - 0531 Paint Color (坐标离散化)
- 区域个数 坐标离散化 BFS算法
- 【坐标离散化】矩形面积求并
- MySql_添加索引
- Android 常用的设计模式
- 机器学习算法 原理、实现与实践 —— 感知机与梯度下降
- g++版本太低了,连iostream都不支持
- Ubuntu16.04安装有道词典(包含第三方软件问题解决方法)
- 坐标离散化,imos
- 多线程编程
- kmp算法 の 初体验
- python3.5 连接mysql本地数据库
- 图小零,一个有点智能的机器人
- 奇怪的捐赠
- 计算机基础
- PAT (Advanced Level) Practise 1001. A+B Format (20)
- 【九度OJ】题目1193:矩阵转置 解题报告