问题提出:
在一个矩形方格中,随机地涂表格,求所涂多边形的周长。
我们如果去求图中所有黑点所组成的多边形的周长呢?小学我们就学过切割法。对于凸边形来说,其周长可以等价于一个大矩形的周长。如下图所示
====》
即周长为4*5=20
但对于凹多边形来说,这就不对了,对于这个凹字就可以看出2*(4+5)!=18。那么应该怎么求呢?对于普通程序员来说,最熟悉的是遍历了,但我可以说的是,有更简单的方法。
我们来分析下,每个小方块矩形有4条边,但相邻的方块会有重复的边。如果我们把重复的边减掉,不就可以得到其周长了吗。先举个例子。对于两个小方块来说,重合的边为2,故其周长为2*4-2=2*(1+2)=6。其实认真想想也很容易想明白的。好了,那我们怎么求重合的边数呢?如果我们把每个小方块黑点当成一个个的点,然后相邻的黑点连成线,那就是我们所说的图了。所重合的边数,就是这个图边数的两倍,即2e(e为此图边的个数)
根据图论的知识,所有点的度之和为边数的2倍,。设黑点数为n,则周长为
。我们来上图中的周长:4*12-(1+3+2+3+4+3+2+2+4+3+2+1)=48-30=18。
以上方法所计算的结果是包含内点的,如第一幅图中右下角有一个内点,即白点周边全是黑的。如果不想包括内点,那么可以先进行对内点处理,全变成黑点,然后再处理。对于第一幅图,如果计算内点的话,即:4*25-(1+2+3+2+2+1+3+2+2+1+1+1+3+3+1+2+3+2+2+3+2+1+1+2+2)=100-48=52。而根据切割法原理,我们可以得到周长为2*(2+4)+2(3+4)-2+2(3+4)+2(4+2)+2=12+14+14+12=52。
代码为:
int perimeter(int **d,int n)
{
// Print(d,n);
int director[4][2]={{1,0},{0,-1},{-1,0},{0,1}};//方位,右-上-左-下
///预处理/
//将内点赋1
bool isOne=true;
for(int i=1;i<n-1;i++){
for(int j=1;j<n-1;j++){
if(d[i][j]==0){
isOne=true;
for(int k=0;k<4;k++){
if(d[i+director[k][0]][j+director[k][1]]!=1){//如果有一个方位不为1,则此点不符合条件
isOne=false;
break;
}
}
if(isOne)
d[i][j]=1;
}
}
}
int Onecount=0,
degree=0;
for(int i=1;i<n-1;i++){
for(int j=1;j<n-1;j++){
if(d[i][j]==1){
Onecount++;//记录值为1的点数
for(int k=0;k<4;k++){
if(d[i+director[k][0]][j+director[k][1]]==1)//记录四个方位值为1的点数
degree++;
}
}
}
}
return 4*Onecount-degree;//返回周长
}