【转】imos-累积和法(原文修正)

原文转自:http://www.hankcs.com/program/algorithm/imos_method.html
但原文示例有误,应修改为一下:

在“怪物地图”的例子中思想与代码有误,应改为:左上角+1,右上角右侧-1,左下角下方-1,右下角右下方+1;若超出tiles则没有+1或-1;根据图示,怪物种类应该是3种,坐标应该为:

    // 左下角坐标
    int b[] = {3,4,5};
    int c[] = {0,1,2};
    // 右上角坐标
    int a[] = {0,3,2};
    int d[] = {3,2,5};

修改后代码为

public void monsterMap() {
    int h=6;
    int w=6;
    int n=3;

    // 左下角坐标
    int b[] = {3,4,5};
    int c[] = {0,1,2};
    // 右上角坐标
    int a[] = {0,3,2};
    int d[] = {3,2,5};

    int[][] tiles = new int[h][w];
    //影响力
    for (int i = 0; i < n; i++) {
        setTiles(tiles, a[i], c[i], +1);
        setTiles(tiles, b[i]+1, d[i]+1, +1);
        setTiles(tiles, a[i], d[i]+1, -1);
        setTiles(tiles, b[i]+1, c[i], -1);
    }
    //横向累加
    for (int i = 0; i < h; i++) {
        for (int j = 1; j < w; j++) {
            tiles[i][j] += tiles[i][j-1];
        }
    }
    //纵向累加
    for (int i = 0; i < w; i++) {
        for (int j = 1; j < h; j++) {
            tiles[j][i] += tiles[j-1][i];
        }
    }
    //查询最大值
    int max=0;
    for (int i = 0; i < h; i++) {
        for (int j = 0; j < w; j++) {
            max = Math.max(tiles[i][j], max);
        }
    }
    System.out.println(max);

}

private void setTiles(int[][] tiles, int h, int w, int v) {
    if (h >= tiles.length || w >= tiles[h].length) {
        return ;
    }
    tiles[h][w] = v;
}

以下为原文部分


在解AOJ 0531 Paint Color时,学到了一个累积和的妙用——imos法,由于原文是日语,所以特意翻译过来。值得一提的是,作者Kentaro Imajo跟鄙人同龄,却已取得如此多的成就,而鄙人一无所成,实在汗颜。

imos法

imos法是将累积和算法拓展到多次元、高次空间的方法。虽然程序竞赛中出题最多不过2次元1次,但是2012年Kentaro Imajo将其用于高次元高次空间,在信号处理/图像处理领域取得了成就。

基础imos法

最简单的imos法是1次元0次系数的求解思想。如图,有三个俄罗斯方块在一起,悬空的部分会掉下去,求从左到右的高度?这个高度就是横坐标固定时,上面矩形高度之和。这就是最简单的imos法。

image

例题

你在经营一个咖啡厅,你的咖啡厅里每个客人在S_i时刻进店,E_i时刻出店。求店里最多有多少客人?(客人最多C个,时刻在T内。如果有多人同时进店出店,先算出店的人)。

朴素的解法

朴素的思想是,计算每个时刻客户的数量,从中找出最大值。但是,复杂度是O(CT)

1.  #include <iostream>
2.  #include <algorithm>
3.  using namespace std;

5.  #define C 4
6.  #define T 10
7.  // 每个客人的进入时间
8.  int S[C] = { 1, 3, 5, 7 };
9.  // 每个客人的离开时间
10.  int E[C] = { 2, 8, 6, 8 };
11.  // 店里的人数
12.  int table[T];

14.  ///////////////////////////SubMain//////////////////////////////////
15.  int main(int argc, char *argv[])
16.  {
17.  memset(table, 0, sizeof(table));
18.  for (int i = 0; i < C; i++) 
19.  {
20.  // 从时间 S[i] 到 E[i] - 1 店里人数计数加一
21.  for (int j = S[i]; j < E[i]; j++) 
22.  {
23.  table[j]++;
24.  }
25.  }
26.  // 找最大値
 27.  cout<< *max_element(table, table + T) << endl;
28.  system("pause");
29.  return 0;
30.  }
31.  ///////////////////////////End Sub//////////////////////////////////

imos法解法

imos法的基本方向是,只统计出入店时刻的人数变化(我个人理解相当于求导),入店+1,出店-1。最终统计的时候,需要将每个时刻加上前一个时刻的统计量(我个人理解相当于求积分),其中的最大值就是所求。记录复杂度O(C),累加复杂度O(T),所以整体复杂度O(C+T)。

1.  #include <iostream>
2.  #include <algorithm>
3.  using namespace std;

5.  #define C 4
6.  #define T 10
7.  // 每个客人的进入时间
8.  int S[C] = { 1, 3, 5, 7 };
9.  // 每个客人的离开时间
10.  int E[C] = { 2, 8, 6, 8 };
11.  // 店里的人数
12.  int table[T];

14.  ///////////////////////////SubMain//////////////////////////////////
15.  int main(int argc, char *argv[])
16.  {
17.  memset(table, 0, sizeof(table));
18.  for (int i = 0; i < C; i++) 
19.  {
20.  table[S[i]]++;  // 入店+1
21.  table[E[i]]--;  // 出店-1
22.  }
23.  // 累加
24.  for (int i = 1; i < T; i++) 
25.  {
26.  table[i] += table[i - 1];
27.  }
28.  // 找最大値
29.  cout<< *max_element(table, table + T) << endl;
30.  system("pause");
31.  return 0;
32.  }
33.  ///////////////////////////End Sub//////////////////////////////////

推广到二次元

imos相对于朴素方法的一个优点就是随着次元增大复杂度的降低越明显。

例题

你在玩一个抓怪兽的游戏,现在你面前是一张W*H的地图,地图里有N种怪物。怪物i只会在左下角为(B_i,C_i),右上角为(A_i,D_i)的矩形区域内出现。求单位区域内最多有多少种怪兽?

image

朴素解法

朴素的解法就是计算每一个单位区域内出现的怪兽数,然后找出最大值。复杂度是O(NWH)。

1.  #include <iostream>
2.  #include <algorithm>
3.  using namespace std;
4.  #define  W  6
5.  #define  H  6
6.  #define  N  4
7.  // 左下角坐标
8.  int B[N] = {3,4,3,5,};
9.  int C[N] = {0,1,2,2,};
10.  // 右上角坐标
11.  int A[N] = {0,3,2,2,};
12.  int D[N] = {3,2,3,5,};
13.  // 地图上的分布结果
14.  int tiles[H][W];

16.  ///////////////////////////SubMain//////////////////////////////////
17.  int main(int argc, char *argv[])
18.  {
19.  memset(tiles, 0, sizeof(tiles));
20.  for (int i = 0; i < N; i++) 
21.  {
22.  // 怪兽 i 出现的范围 [(A[i],C[i]), (B[i],D[i])) 内的计数加一
23.  for (int y = C[i]; y < D[i]; y++) 
24.  {
25.  for (int x = A[i]; x < B[i]; x++) 
26.  {
27.  tiles[y][x]++;
28.  }
29.  }
30.  }
31.  // 求最大値
32.  cout<< *max_element(tiles[0], tiles[0] + H * W) << endl;
33.  system("pause");
34.  return 0;
35.  }
36.  ///////////////////////////End Sub//////////////////////////////////

imos法解法

此例思想与代码有误,修改方法见文字开头

矩形的左上角 (A[i],C[i]) +1 ,右上角 (A[i],D[i]) −1,左下角 (B[i],C[i]) −1 ,右下角(B[i],D[i]) +1 ,统计最终结果之前累加。加一减一 O(N),累加(WH) 整体复杂度 O(N+WH) 。

1.  #include <iostream>
2.  #include <algorithm>
3.  using namespace std;
4.  #define  W  6
5.  #define  H  6
6.  #define  N  4
7.  // 左下角坐标
8.  int B[N] = {3,4,3,5,};
9.  int C[N] = {0,1,2,2,};
10.  // 右上角坐标
11.  int A[N] = {0,3,2,2,};
12.  int D[N] = {3,2,3,5,};
13.  // 地图上的分布结果
14.  int tiles[H][W];

16.  ///////////////////////////SubMain//////////////////////////////////
17.  int main(int argc, char *argv[])
18.  {
19.  memset(tiles, 0, sizeof(tiles));
20.  // 影响力计算 (图 3)
21.  for (int i = 0; i < N; i++) 
22.  {
23.  tiles[C[i]][A[i]]++;
24.  tiles[C[i]][B[i]]--;
25.  tiles[D[i]][A[i]]--;
26.  tiles[D[i]][B[i]]++;
27.  }
28.  // 横向累积和 (图 4, 5)
29.  for (int y = 0; y < H; y++) 
30.  {
31.  for (int x = 1; x < W; x++) 
32.  {
33.  tiles[y][x] += tiles[y][x - 1];
34.  }
35.  }
36.  // 纵向累积和 (图 6, 7)
37.  for (int y = 1; y < H; y++) 
38.  {
39.  for (int x = 0; x < W; x++) 
40.  {
41.  tiles[y][x] += tiles[y - 1][x];
42.  }
43.  }

45.  cout<< *max_element(tiles[0], tiles[0] + H * W) << endl;
46.  system("pause");
47.  return 0;
48.  }
49.  ///////////////////////////End Sub//////////////////////////////////
image

图三:影响力计算

image

图四:横向累加

image

图五:横向累加结果

image

图六:纵向累加

image

图七:纵向累加结果

更高级的用法

暂时用不到,记录一个地址,有空再看。

Reference

http://imoz.jp/algorithms/imos_method.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容