Arable Area

Arable Area

Time Limit:1000MS  Memory Limit:65536K
Total Submit:7 Accepted:1

Description

The prime minister has recently bought a piece of valuable agricultural land, which is situated in a valley forming a regular grid of unit square fields. ACM would like to verify the transaction, especially whether the price corresponds to the market value of the land, which is always determined as the number of unit square fields fully contained in it.
Your task is to write a program that computes the market value. The piece of land forms a closed polygon, whose vertices lie in the corners of unit fields.
For example, the polygon in the picture (it corresponds to the first scenario in Sample Input) contains three square fields.

 

Input

The input consists of several test scenarios. Each scenario starts with a line containing one integer number N (3 ≤ N ≤ 100), the number of polygon vertices. Each of the following N lines contains a pair of integers Xi and Yi, giving the coordinates of one vertex. The vertices are listed in the order they appear along the boundary of the polygon. You may assume that no coordinate will be less then −100 or more than 100 and that the boundary does not touch or cross itself.
The last scenario is followed by a line containing single zero.

Output

For each scenario, output one line with an integer number — the number of unit squares that are completely inside the polygon.

Sample Input

 

4
1 1
5 3
5 4
3 5
5
3 3
2 5
3 4
5 2
1 1
5
0 0
0 -50
-50 -51
-51 -50
-50 0
0

 

Sample Output  

3
1
2500
题意:依次输入多边形的坐标,围成封闭多边形(由intput可以看出包括了凹多边形),统计在该多边形内的方块个数。
解题思路:该题当时我们想了很久都没想出解决办法,后来看见了刘女佳的《算法竞赛入门经典》中P85,有关利用有向面积计算三角形内部点的个数。
有此启发做这道题。想解决这题有如下几个难题:
1.因为存在凹凸多边形,因此要先判断是凹多边形还是凸多边形 
	方法:利用向量叉积。
	若 P × Q > 0 , 则P在Q的顺时针方向。
若 P × Q < 0 , 则P在Q的逆时针方向。
若 
P × Q = 0 , 则P与Q共线,但可能同向也可能反向。
	可是;利用这一性质只能判定是否是顺逆时针。hdu2108的题目可以利用该方法来求解,因为测试数据都是单一方向的,
	可该题不同(如测试数据3),有此则加以改进,不管正负,只当依次计算相邻2点的叉积,若前后2个叉积的符号不同,
	则判定为凹多边形,反之为凸多边形。
2.判断完多边形后,当为凸多边形时
	方法:利用《算法竞赛入门经典》中的向量积方法,可是貌似书上用面积判定的方法不太正确,本人认为书中
	S△ABC=S△ABO+S△BCO+S△CAO判定是否在多边形内因改为|S△ABC|=|S△ABO|+|S△BCO|+|S△CAO|,
	经过笔算与编程调试,不管是否在多边形内部,等式两边的值总是相等的。因为有向面积存在正负(多边形各定点依次为顺时针时为负,反之为正)
	当测试点在多边形内时,3个小三角形有向面积都是与大三角形一致的正负号,而当在多边形外时,
	总有一个小三角形的有向面积于大三角形的正负号不同,因此算出来的和总是相同的。有此利用加一个绝对值再求和可以判定是否在多边形内。
3.当是凹多边形时的判定方法
	方法:我们应当两个同时判断。即判断该点是否同时在多边形的连续两个三角形之中,相当于是求两个三角形的交集,直到完成多边形封闭。
	例如,判断P点是否在多边形ABCD之中,依次判断P是否在△ABC-△BCD、△BCD-△CDA、△CDA-△DAB、△DAB-△ABC各个成对三角形中,
	P在△ABC-△BCD中表示P在△ABC-△BCD的交集之中。若存在一点是相邻2个三角形的交集中,则该点一定在凹多边形内
	这样就可以判断一个点是否在一个凹多边形内部了。
总结:书中的知识不一定是全对的,我们应该亲手写下代码,并加以拓宽,这样才能学到更多新的知识,毕竟自己写的代码才是自己的。

代码:该为浙江理工大学ACM 3374 arable area 代码,是超时的代码。。。有不足之处请多多指出。

 

#include <stdio.h>
#include<string.h>
#include<math.h>
#define MAX 200+20
struct point
{
 int x,y;
}p[MAX];
int a[MAX][MAX];
int n,Aarea;

int area_all(int x,int y)
{
 int sum_area=0,i;
 for(i=0;i<n;i++)
 {
  if(i==0) sum_area=sum_area+p[n-1].x*p[i].y+p[i].x*y+x*p[n-1].y-p[n-1].y*p[i].x-p[i].y*x-y*p[n-1].x;
  else sum_area=sum_area+p[i-1].x*p[i].y+p[i].x*y+x*p[i-1].y-p[i-1].y*p[i].x-p[i].y*x-y*p[i-1].x; 
 }
 return sum_area;
}

int is_conver()
{
 int i,sum,sum_t;
 p[n].x=p[0].x;
 p[n].y=p[0].x;
 p[n+1].x=p[1].x;
 p[n+1].y=p[1].y;
 p[n+2].x=p[2].x;
 p[n+2].y=p[2].y;
 sum=(p[1].x-p[0].x)*(p[2].y-p[0].y) - (p[2].x-p[0].x)*(p[1].y-p[0].y);
 for(i=1;i<n;i++)
 {
  sum_t=(p[i+1].x-p[i].x)*(p[i+2].y-p[i].y) - (p[i+2].x-p[i].x)*(p[i+1].y-p[i].y);
  if(sum<0&&sum_t>=0||sum_t<0&&sum>=0) return 0;
  sum=sum_t;
 }
 return 1;
}

int area(int x1,int y1,int x2,int y2,int x3,int y3)
{
 return x1*y2+x2*y3+x3*y1-y1*x2-y2*x3-y3*x1;
}
int is_inarea_convare(int x,int y)
{
 int all_area1,all_area2,sum_area=0,sum_area_t,i,j,flag1,flag2;
 for(i=0;i<n;i++)
 {
  flag1=0;flag2=0;
  all_area1=area(p[i].x,p[i].y,p[i+1].x,p[i+1].y,p[i+2].x,p[i+2].y);
  all_area2=area(p[i+1].x,p[i+1].y,p[i+2].x,p[i+2].y,p[i+3].x,p[i+3].y);
  for(j=i;j<i+3;j++)
  {
   if(j==i+2)sum_area_t=area(p[j].x,p[j].y,p[j-2].x,p[j-2].y,x,y);
   else sum_area_t=area(p[j].x,p[j].y,p[j+1].x,p[j+1].y,x,y);
   if(sum_area_t>0&&all_area1<0||sum_area_t<0&&all_area1>0) {flag1=1;break;}//return false;
  }
  for(j=i+1;j<i+4;j++)
  {
   if(j==i+3)sum_area_t=area(p[j].x,p[j].y,p[j-2].x,p[j-2].y,x,y);
   else sum_area_t=area(p[j].x,p[j].y,p[j+1].x,p[j+1].y,x,y);
   if(sum_area_t>0&&all_area2<0||sum_area_t<0&&all_area2>0) {flag2=1;break;}//return false;
  }
  if(flag1==0&&flag2==0) return 1;
 }
 return 0;
}

int is_inarea_conver(int x,int y)
{
 int sum_area_t=0,i;
 for(i=0;i<n;i++)
  sum_area_t=abs(area(p[i].x,p[i].y,p[i+1].x,p[i+1].y,x,y))+sum_area_t;   
 if(sum_area_t!=abs(Aarea)) return 0;
 else return 1;
}

int main()
{
 int i,j,max_x,max_y,min_x,min_y;
 while(scanf("%d",&n))
 {
  if(!n) break;
  int count=0;
  memset(a,0,sizeof(a));
  for(i=0;i<n;i++)
  {
   scanf("%d%d",&p[i].x,&p[i].y);
   if(i==0) {max_x=p[i].x,max_y=p[i].y;min_x=p[i].x;min_y=p[i].y;}
   if(p[i].x<min_x) min_x=p[i].x;
   if(p[i].x>max_x) max_x=p[i].x;
   if(p[i].y<min_y) min_y=p[i].y;
   if(p[i].y>max_y) max_y=p[i].y;
  }
  //printf("min_x=%d,min_y=%d,max_x=%d,max_y=%d/n",min_x,min_y,max_x,max_y);
  
  //printf("Aarea=%d/n",Aarea);
  if(is_conver())
  { //printf("conver/n");
   Aarea=area_all(p[0].x,p[0].y);
   for(j=min_y;j<=max_y;j++)
    for(i=min_x;i<=max_x;i++) 
     if(is_inarea_conver(i,j))
      a[j+110][i+110]=1;
  }
  else
  {//printf("convare/n");
   for(j=min_y;j<=max_y;j++)
    for(i=min_x;i<=max_x;i++) 
     if(is_inarea_convare(i,j))
      a[j+110][i+110]=1;
  }
  /*for(j=max_y+110;j>=min_y+110;j--)
  {
   for(i=min_x+110;i<=max_x+110;i++)
    printf("%d",a[j][i]);
  printf("/n");
  }*/
  for(i=min_x+110;i<=max_x+110;i++)
   for(j=min_y+110;j<=max_y+110;j++)
    if(a[i][j]&&a[i+1][j]&&a[i][j+1]&&a[i+1][j+1]) count++;
  printf("%d/n",count);
 }
 return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值