Hdu3468_Median Filter(树状数组求中位数)

本文介绍了一种基于树状数组的中值滤波算法实现,该算法在n*n矩阵中进行中值滤波,通过滑动窗口计算子矩阵的中位数并更新矩阵中心值。文章详细解释了算法思路,包括窗口滑动过程和中位数查找方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

中值滤波,求在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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值