CCF 202104-2:邻域均值--C++

2023-12-13 17:33:43

#include<iostream>
#include<bits/stdc++.h>

using namespace std;


int A[601][601];
int n;//长宽都为n个像素

double FindNeighborSum(int i,int j,int r,int A[][601])
{
	int sum=0;//像素和 
	int gs=0;//领域 中的像素个数 
	for(int x=i-r;x<=i+r;x++)//找到每一个领域像素点 
	{
		for(int y=j-r;y<=j+r;y++)
		{
			if(x>=0&&x<n)
			{
				if(y>=0&&y<n)
				{
				sum+=A[x][y];
				gs++;
				}
				
			}
		}
	}
	
	double result=(double)sum/gs;//要用double不能用int,不然等于t的数量会变多 
	return result;//
}

int main()
{
    
    
	int L;//像素的取值范围
	int r;//领域的范围
	int t;//阈值,当领域内的均值小于或等于阈值t时是较暗区域
	
	cin>>n>>L>>r>>t;
	 
	 for(int i=0;i<n;i++)
	 {
	 	for(int j=0;j<n;j++)
	 	cin>>A[i][j];
	 }
    
    
    int sum=0;//记录较暗区域个数 
    for(int i=0;i<n;i++)
    {
    	for(int j=0;j<n;j++)//对每一个像素点分析 
    	{
    		if(FindNeighborSum(i,j,r,A)<=t) sum++;
		}
	}
	
	cout<<sum;
    
    return 0;
}

暴力求解:70分,要返回一个double类型的值,不然的话有些不是较暗区域的点也会被计为较暗区域

原本我想 分区域来运算,当邻域像素点个数为最大值(2*r+1)*(2*r+1)时用二维差分,否则用暴力

但是还是会超时

#include<iostream>
#include<bits/stdc++.h>

using namespace std;


int A[601][601];
int n;//长宽都为n个像素

int d[601][601];//记录(i,j)点的前缀和 

double FindNeighborSum(int i,int j,int r,int A[][601])
{
	int suml=0;//像素和 
	int gs=0;//领域 中的像素个数 
	for(int x=i-r;x<=i+r;x++)//找到每一个领域像素点 
	{
		for(int y=j-r;y<=j+r;y++)
		{
			if(x>=0&&x<n)
			{
				if(y>=0&&y<n)
				{
				suml+=A[x][y];
				gs++;
				}
				
			}
		}
	}
	
	double result=(double)suml/gs;//要用double不能用int,不然等于t的数量会变多 
	return result;//
}

int main()
{
    
    
	int L;//像素的取值范围
	int r;//领域的范围
	int t;//阈值,当领域内的均值小于或等于阈值t时是较暗区域
	
	memset(d,0,sizeof d);//将d清零 
	
	cin>>n>>L>>r>>t;
	 
	 for(int i=0;i<n;i++)
	 {
	 	for(int j=0;j<n;j++)
	 	{
	 		cin>>A[i][j];
	 	
	 	d[i][j]=d[i][j-1]+d[i-1][j]-d[i-1][j-1]+A[i][j]; 
	 	//cout<<d[i][j]<<endl;
		 }
	 	
	 	
	 }
    
    
    int sum=0;//记录较暗区域个数 
    int NeighborSum=0;//记录邻域中像素数值之和 
    
    double NeighborAvg=0;
    
    for(int i=0;i<n;i++)
    {
    	for(int j=0;j<n;j++)//对每一个像素点分析 
    	{	
    		
    		if(i-r>=0&&i+r<n&&j-r>=0&&j+r<n)//分区域来运算,当邻域像素点个数为最大值(2*r+1)*(2*r+1)时用差分,否则用暴力
			{
				
				NeighborSum=d[i+r][j+r]-d[i+r][j-r-1]-d[i-r-1][j+r]+d[i-r-1][j-r-1];
				
				NeighborAvg=(double)NeighborSum/((2*r+1)*(2*r+1));
				if(NeighborAvg<=t) sum++;
	     	}
			 
			 else
			 {//邻域的上下左右有些地方不全 
			 	if(FindNeighborSum(i,j,r,A)<=t) sum++;	  
			  } 
    		
		}
	}
	
	cout<<sum;
    
    return 0;
}

从上面的分区域到下面的满分优化,关键是怎么得到邻域的像素点个数,上面的分区域方法如果所判断的像素点(i,j)的邻域没有缺少,即邻域像素点个数达到最大(2*r-1)*(2*r-1),如果(i,j)的邻域不完整,那就暴力的一个一个判断使得gs++来得到邻域中像素点的个数。

可以通过邻域的上下左右来求得邻域中像素点的个数

如图,如果此时红色笔圈起来的数7是当前判断到的像素,设为(i,j),r=2, 那么(i,j)的邻域就应该是如图画的正方形,红色直线=left=j-r;? 橙色直线=right=j+r ,蓝色直线=top=i-r;绿色直线=buttom=i+r;

所以这个邻域中像素点的个数 等于 (right-left+1)*(buttom-top+1)

这是理想的情况,即邻域是完整的

当邻域不完整时,应该通过判断来调整上下左右的取值,但是像素点个数求法还是一样的

? ? ? ? ? ? ? if(i-r<0)//上边不够
?? ??? ??? ??? ?top=0;
?? ??? ??? ??? ?else//上边够那么可能下边不够?
?? ??? ??? ??? ?{
?? ??? ??? ??? ?if(i+r>=n)//下边不够?
?? ??? ??? ??? ?buttom=n-1;?? ?
?? ??? ??? ??? ?}
?? ??? ??? ??? ?
?? ??? ??? ??? ?
?? ??? ??? ??? ?if(j-r<0)//左边不够
?? ??? ??? ??? ?left=0;
?? ??? ??? ??? ?else?? ? ?
?? ??? ??? ??? ?if(j+r>=n)//右边不够
?? ??? ??? ??? ?right=n-1;?

再用前缀和来求解一个区域中像素点的数值和


优化:用二维差分,记录一下我的第一次自己优化?

#include<iostream>
#include<bits/stdc++.h>

using namespace std;


int A[601][601];
int n;//长宽都为n个像素

int d[601][601];//记录(i,j)点的前缀和 

int main()
{
    
    
	int L;//像素的取值范围
	int r;//领域的范围
	int t;//阈值,当领域内的均值小于或等于阈值t时是较暗区域
	
	memset(d,0,sizeof d);//将d清零 
	
	cin>>n>>L>>r>>t;
	 
	 for(int i=0;i<n;i++)
	 {
	 	for(int j=0;j<n;j++)
	 	{
	 		cin>>A[i][j];
	 	
	 	d[i][j]=d[i][j-1]+d[i-1][j]-d[i-1][j-1]+A[i][j]; 
	 	//cout<<d[i][j]<<endl;
		 }
	 	
	 	
	 }
    
    
    int sum=0;//记录较暗区域个数 
    int NeighborSum=0;//记录邻域中像素数值之和 
    
    double NeighborAvg=0;
    
    int Neighbor=0;//记录邻域中像素个数 
    
    int left=0,right=0,top=0,buttom=0;//记录邻域的上下左右,方便计数 
    
    for(int i=0;i<n;i++)
    {
    	for(int j=0;j<n;j++)//对每一个像素点分析 
    	{
    		
                //首先将邻域当作理想情况,后面通过判断再调整
    		    top=i-r;
			 	buttom=i+r;
			 	left=j-r;
			 	right=j+r;
			 	
			 	
			 	if(i-r<0)//上边不够
				top=0;
				else//上边够那么可能下边不够 
				{
				if(i+r>=n)//下边不够 
				buttom=n-1;	
				}
				
				
				if(j-r<0)//左边不够
				left=0;
				else	  
				if(j+r>=n)//右边不够
				right=n-1; 
				
				Neighbor=(buttom-top+1)*(right-left+1); //邻域中像素点个数 
					  
				
				NeighborSum=d[buttom][right]-d[buttom][left-1]-d[top-1][right]+d[top-1][left-1];
				//cout<<NeighborSum<<endl;
				NeighborAvg=(double)NeighborSum/Neighbor;
				if(NeighborAvg<=t) sum++;
		
		  
				  
			  } 
    		
	}
	
	cout<<sum;
    
    return 0;
}

我自己的理解,之前看过一篇特别好的差分法的文章,可惜找不到了

差分法就是在输入的时候求得对应位置的前缀和,当你需要对某个区间或区域进行加减时不用一个一个加减,直接对前缀和数组操作

一维差分:
int n=10;
for(int i=0;i<n;i++)
{
cin>>A[i];
d[i]=d[i-1]+A[i];//前缀和数组,代表第i位以及前面所有数据的和
}

//对[1,5]的数据全部加1
d[1]+=1;
d[5]-=1;

//只需要对区间两端的前缀和数组进行操作即可
//A[i]=d[i]-d[i-1];//得到新的加一之和的值

例题:非零段划分202109-2 非零段划分--C++-CSDN博客? ??

二维差分:
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
cin>>A[i][j];
d[i][j]=d[i][j-1]+d[i-1][j]-d[i-1][j-1]+A[i][j];
}
}

当i=3,j=3时,d[i][j]就是如图左上角的所有数之和

这样我们通过输入就可以得到每一个数的二维前缀和,当我们想要求一个区域的所有数之和(在本题中相对于求邻域中的所有数值之和),当我们想要求红色区域的所有数之和,可以用黄色区域所有数之和即d[4][5],减去蓝色区域所有数之和即d[4][2],再减去粉色区域所有数之和即d[1][5],重复减去的区域要加回来,加上d[2][2],就可以得到想要求的区域的所有数之和

差分法~超详细(公式+原理+例题)-CSDN博客

文章来源:https://blog.csdn.net/weixin_69266073/article/details/134879875
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。