题意:
中值滤波,求在n * n的矩阵中,内(n-2r) * (n-2r)的中值滤波后的子矩阵。
中值滤波的方式就是对于每一个可以取到的以(i,j)为矩阵中心的(2r+1)(2*r+1)的子矩阵,将子矩阵中所有数排序后的中位数作为新值,新值不影响后面的值。
思路:
设窗口为一个边长为(2*r+1)的正方形,每个正方形的中心都要被它范围内的中位数替换。窗口在滑动的过程中,比如向右滑1单位,则左边一列出去,右边一列进来,再求中位数更新中心元素,可以看到除了边缘的两列中间没有变动,用树状数组来增加和删除,求中位数,就很舒服了。
窗口从左上角向右,向下,一条龙。
#include <cstdio>
#include <cstring>
#include <algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int maxn = 500+5;
using namespace std;
int n,r, maxnum;
int G[maxn][maxn], ans[maxn][maxn], C[1<<20];
inline int lowbit(int x){return x & -x;}
void add(int x, int v){for(int i = x; i <= maxnum; i+= lowbit(i)) C[i]+= v;}
int sum(int x){
int s = 0;
for(int i = x; i > 0; i-= lowbit(i)) s+= C[i];
return s;
}
int find_kth_small(int k){
int ans = 0, cnt = 0;
for(int i = 20; i >= 0; --i){
ans+= (1<<i);
if(ans > maxnum||cnt + C[ans] >= k) ans-= (1<<i);
else cnt+= C[ans];
}
return ans+1;
}
void solve(){
int L = 2*r+1; // 边长
int k = (L*L)/2 + 1; // 求第 k 大的数,即中位数(总个数为奇数)
for(int i = 1; i <= L; ++i)
for(int j = 1; j <= L; ++j)
add(G[i][j], 1);
for(int i = r+1; i <= n-r; ++i){
if((i-r)&1){ // 从左向右滚动处理
if(i > r+1){
// 向下一行,更新窗口的上面一行
for(int j = 1; j <= L; ++j){
add(G[i-r-1][j], -1);
add(G[i+r][j], 1);
}
}
ans[i][r+1] = find_kth_small(k);
// 向右
for(int j = r+2; j <= n-r; ++j){
for(int i2 = i-r; i2 <= i+r; ++i2){
add(G[i2][j-r-1], -1);
add(G[i2][j+r], 1);
}
ans[i][j] = find_kth_small(k);
}
}
else{ // 从右向左滚动处理
// 向下一行,更新窗口的上面一行
for(int j = n; j >= n-L+1; --j){
add(G[i-r-1][j], -1);
add(G[i+r][j], 1);
}
ans[i][n-r] = find_kth_small(k);
for(int j = n-r-1; j >= r+1; --j){
for(int i2 = i-r; i2 <= i+r; ++i2){
add(G[i2][j+r+1], -1);
add(G[i2][j-r], 1);
}
ans[i][j] = find_kth_small(k);
}
}
}
}
int main()
{
freopen("in.txt","r",stdin);
while(scanf("%d%d",&n,&r) == 2){
memset(C, 0, sizeof(C));
maxnum = 0;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j){
scanf("%d",&G[i][j]);
++G[i][j];
if(maxnum < G[i][j]) maxnum = G[i][j];
}
solve();
for(int i = r+1; i <= n-r; ++i){
for(int j = r+1; j <= n-r; ++j)
printf("%d ",ans[i][j]-1);
printf("\n");
}
}
fclose(stdin);
return 0;
}