まず、次のような形式の入出力ができるように、提示されたサンプルを改造しました。
●入力
W : 入力されるデータの幅
H : 入力されるデータの高さ
r : 最小値を取る範囲(課題で指定されたもの)
data(i,j) : SRC(i,j)に代入するデータ
コード:
W H r
data(0,0) data(1,0) ... data(W-1,0)
data(0,1) data(1,1) ... data(W-1,1)
...
data(0,H-1) data(1,H-1) ... data(W-1,H-1)
●出力
data(i,j) : DST(i,j)に代入するデータ
コード:
data(0,0) data(1,0) ... data(W-1,0)
data(0,1) data(1,1) ... data(W-1,1)
...
data(0,H-1) data(1,H-1) ... data(W-1,H-1)
●サンプル入力
コード:
3 3 1
1 2 3
4 5 6
7 8 9
●サンプル出力
改造したコードがこちらになります。
► スポイラーを表示
コード:
#include <cstdio>
#include <algorithm>
#include <cstdlib>
//※画像バッファは入力出力共にW*H画素分用意されているものとする
//const int W = 画像幅:
//const int H = 画像高さ;
int W,H;
unsigned char src[4096][4096];
unsigned char dest[4096][4096];
//画素アクセス
//inline unsigned char &SRC(x,y){ return 入力画像データバッファの(x,y)の位置; }
//inline unsigned char &DST(x,y){ return 出力画像データバッファの(x,y)の位置; }
#define SRC(x,y) src[(y)][(x)]
#define DST(x,y) dest[(y)][(x)]
//シンプルな実装
void MinFilter( int r )
{
r = (std::max)( abs( r ), 1 ); //※rは1以上の整数
for( int y=0; y<H; y++ )
{
const int top = (std::max)( y-r, 0 );
const int bottom = (std::min)( y+r, H-1 );
for( int x=0; x<W; x++ )
{
const int left = (std::max)( x-r, 0 );
const int right = (std::min)( x+r, W-1 );
//(left,top)-(right,bottom)矩形内の最小値を見つける
unsigned char MinVal = 255;
for( int yy=top; yy<=bottom; yy++ )
{
for( int xx=left; xx<=right; xx++ )
{
if( SRC(xx,yy) < MinVal )
{ MinVal = SRC(xx,yy); }
}
}
//結果格納
DST(x,y) = MinVal;
}
}
}
int main(void) {
int r;
scanf("%d%d%d",&W,&H,&r);
for(int i=0;i<H;i++) {
for(int j=0;j<W;j++) {
int temp;
scanf("%d",&temp);
src[i][j]=(unsigned char)(temp&0xFF);
}
}
MinFilter(r);
for(int i=0;i<H;i++) {
for(int j=0;j<W;j++) {
printf("%d%c",dest[i][j],j+1<W?' ':'\n');
}
}
return 0;
}
そして、肝心の考え方としては、セグメント木を使います。
まず、各行ごとに、横は指定された範囲、縦はその行だけという範囲の最小値を求め、一旦DSTに代入しておきます。
次に、各列ごとに、横はその列だけ、縦は指定された範囲という範囲の最小値をDSTから求め、結果をDSTに代入します。
これでDSTに求める結果が代入されます。
コード:
#include <cstdio>
#define W_MAX 4096
#define H_MAX 4096
int W,H;
unsigned char src[4096][4096];
unsigned char dest[4096][4096];
#define SRC(x,y) (src[(y)][(x)])
#define DST(x,y) (dest[(y)][(x)])
/**
* 指定した範囲の最小値を求めるセグメント木を構築する
* @param segtree セグメント木データへのポインタ
* @param segtreeSize セグメント木のサイズ(2の非負整数乗でないといけない)
*/
void MakeSegmentTree(unsigned char* segtree,int segtreeSize) {
// セグメント木を構築する
for(int i=segtreeSize-2;i>=0;i--) {
segtree[i]=segtree[i*2+1];
if(segtree[i*2+2]<segtree[i])segtree[i]=segtree[i*2+2];
}
}
/**
* セグメント木から指定した範囲の最小値を求める
* @param segtree セグメント木のデータ
* @param segtreeSize セグメント木のサイズ(2の非負整数乗でないといけない)
* @param start 範囲の開始点(含まれる)
* @param end 範囲の終了点(含まれない)
* @return 最小値
*/
unsigned char GetMinFromSegmentTree(const unsigned char* segtree,
int segtreeSize,int start,int end,int now=0,int kstart=0,int kend=-1) {
if(kend<0)kend=segtreeSize;
// 範囲外なので無視する
if(end<=kstart || kend<=start)return 255;
// 範囲全てが含まれるのでそのまま返す
if(start<=kstart && kend<=end)return segtree[now];
// 下の階層に丸投げする
unsigned char candidate1,candidate2;
candidate1=GetMinFromSegmentTree(segtree,segtreeSize,start,end,
now*2+1,kstart,(kstart+kend)/2);
candidate2=GetMinFromSegmentTree(segtree,segtreeSize,start,end,
now*2+2,(kstart+kend)/2,kend);
return candidate1<candidate2?candidate1:candidate2;
}
void MinFilter(int r) {
if(r<1)r=1;
unsigned char* segtree;
// セグメント木のサイズを決定する
int segtreeSize;
segtreeSize=(W>=H?W:H);
for(;;) {
int nextss=segtreeSize-(segtreeSize & (-segtreeSize));
if(nextss==0)break;
segtreeSize=nextss;
}
if(segtreeSize!=r)segtreeSize*=2;
segtree=new unsigned char[segtreeSize*2];
unsigned char* segtreeData=segtree+segtreeSize-1;
// 横方向の最小値を求める
for(int y=0;y<H;y++) {
// セグメント木にデータを設定する
for(int x=0;x<W;x++)segtreeData[x]=SRC(x,y);
for(int x=W;x<segtreeSize;x++)segtreeData[x]=255;
MakeSegmentTree(segtree,segtreeSize);
// セグメント木から最小値を求める
for(int x=0;x<W;x++) {
int start=x-r;
int end=x+r+1;
if(start<0)start=0;
if(end>W)end=W;
DST(x,y)=GetMinFromSegmentTree(segtree,segtreeSize,start,end);
}
}
// 横方向の最小値の最小値を、縦方向で求める
for(int x=0;x<W;x++) {
// セグメント木にデータを設定する
for(int y=0;y<H;y++)segtreeData[y]=DST(x,y);
for(int y=H;y<segtreeSize;y++)segtreeData[y]=255;
MakeSegmentTree(segtree,segtreeSize);
// セグメント木から最小値を求める
for(int y=0;y<H;y++) {
int start=y-r;
int end=y+r+1;
if(start<0)start=0;
if(end>H)end=H;
DST(x,y)=GetMinFromSegmentTree(segtree,segtreeSize,start,end);
}
}
delete[] segtree;
}
int main(void) {
int r;
scanf("%d%d%d",&W,&H,&r);
for(int i=0;i<H;i++) {
for(int j=0;j<W;j++) {
int temp;
scanf("%d",&temp);
src[i][j]=(unsigned char)(temp&0xFF);
}
}
MinFilter(r);
for(int i=0;i<H;i++) {
for(int j=0;j<W;j++) {
printf("%d%c",dest[i][j],j+1<W?' ':'\n');
}
}
return 0;
}
添付のテストケースでテストしたところ、最初のプログラムで答えが出せなかったcase16.txtを除き、出力が一致しました。