HDU 3648 Median Filter

来源:互联网 发布:逐鹿软件官网 编辑:程序博客网 时间:2024/04/28 16:43


弱渣花了好几天终于把这题A了_(:з」∠)_

可能因为大牛们认为这题太简单吧,网上的题解都不怎么带注释。特别是那个S型走法,看的博(ruo)主(cai)一愣一愣的_(:з」∠)_

因此AC后打算整理下大牛们的题解并加上注释,以便查阅_(:з」∠)_

主要参考了下面几篇题解:

http://blog.csdn.net/zhaofukai/article/details/7841327

http://blog.csdn.net/chromer_cn/article/details/7892048


原题:http://acm.hdu.edu.cn/showproblem.php?pid=3648

题意:给出一个n*n的矩阵,现在对于(r+1, r+1) to (n-r, n-r)中每个点,以其为中心的规模为(2r+1)*(2r+1)的子矩阵,替换其中间元素为该子矩阵的中位数。

n<=500 元素<=10^6。


A这道题前首先要知道什么是树状数组和怎么样用树状数组快速求第k小的值。

博(ruo)主(cai)从书上学的树状数组,在这就不安利书籍了,网上也有很多很棒的资料。

至于怎么样用树状数组求第k小的值,博(ruo)主(cai)个人认为这篇讲的挺好的 http://www.cnblogs.com/wuyiqi/archive/2011/12/25/2301071.html 

看的时候注意树状数组[1...i]的区间和表示的是“树状数组中小于等于i的数的个数和”就行了。(防止有新手误解,强调一下上面的“区间和”指的是用树状数组求出来的区间和,而不是简单的从下标1开始将每个元素相加知道下标i !!!)


准备好了知识点,就可以做题了。

实际上思路很好想的嘛_(:з」∠)_,将子阵中的数插入到树状数组求中位数,然后再插几行删几行求新子阵的新中位数。不过听说普通的走法会TLE,走S型路线可以最大限度的利用旧子阵,然后就AC了_(:з」∠)_。


有人可能会问k的范围怎么定,这种问题自己画个子阵就不清楚了嘛...


#include<bits/stdc++.h>#define maxv 1000010 #define maxn 510using namespace std;int N,R,mxv;int table[maxn][maxn],ans[maxn][maxn];int bit[maxv];//改了一下的树状数组模板,没什么好说的吧?inline int lowbit(int x){return x&-x;}void add(int i,int x){while(i<=mxv){bit[i]+=x;i+=lowbit(i);}}int find_kth(int k){int ret=0,cnt=0;for(int i=20;i>=0;i--){ret+=(1<<i);if(ret>=mxv||cnt+bit[ret]>=k) ret-=(1<<i);else cnt+=bit[ret];}return ret+1;}void SOLVE(){int mid=2*R*R+2*R+1;//子阵的中位数是子阵中第mid小的数bool pass=true;//S型走法中向右走的那些行for(int i=R+1;i<=N-R;i++)if(pass){//向右走pass=false;for(int j=R+1;j<=N-R;j++){if(j==R+1)//第一格特殊处理,即往下走,删旧的一行加新的一行for(int k=j-R;k<=j+R;k++)add(table[i-R-1][k],-1),add(table[i+R][k],1);else//向右走,删旧的一列加新的一列for(int k=i-R;k<=i+R;k++)add(table[k][j-R-1],-1),add(table[k][j+R],1);ans[i][j]=find_kth(mid);//求中位数}}else{//向左走pass=true;for(int j=N-R;j>=R+1;j--){if(j==N-R)//第一格特殊处理,即往下走,删旧的一行加新的一行for(int k=j-R;k<=j+R;k++)add(table[i-R-1][k],-1),add(table[i+R][k],1);else //向左走,删旧的一列加新的一列for(int k=i-R;k<=i+R;k++)add(table[k][j+R+1],-1),add(table[k][j-R],1);ans[i][j]=find_kth(mid);//求中位数}}for(int i=R+1;i<=N-R;i++)for(int j=R+1;j<=N-R;j++)printf("%d%s",--ans[i][j],j==N-R?" \n":" ");}bool INPUT(){scanf("%d%d",&N,&R);if(!N&&!R) return false;mxv=-1;for(int i=1;i<=N;i++)for(int j=1;j<=N;j++)scanf("%d",&table[i][j]),mxv=max(mxv,++table[i][j]);//0的lowbit还是0,自然没办法插入,处理一下就可以了(*)memset(bit,0,sizeof bit);//插入第一个子阵,不过为了SOLVE中的走法统一,这里插入的是第一个子阵的上面一格的那个子阵for(int i=0;i<=(R<<1);i++)for(int j=1;j<=(R<<1)+1;j++)add(table[i][j],1);return true;}void MAIN(){for(int i=0;i<maxn;i++) table[0][i]=1;//和(*)一样的道理while(INPUT())SOLVE();}int main(){//freopen("in#pro.txt","r",stdin);//freopen("out.txt","w",stdout);MAIN();return 0;}


0 0
原创粉丝点击