Gym - 101234C Crazy Dreamoon 解题报告(有关精度)

题目大意:大鹏经常在ACM比赛时做不出题,于是就开始画一些线段,无聊的他想知道他画的线段一共穿过了多少正方形格子,但由于他比较垃圾,所以想请你帮忙计算(多条直线经过一个格子算一个)。

思路:当初拿过来这道题很蒙,以为是靠什么公式,但是推了半天也没推出来,补题的时候才知道是每一个单位横坐标一个个讨论,类似于分治法思想,但是这道题仍然不能AC,看了看大佬的代码,发现自己缺少精度的控制,垃圾的我赶紧找了篇博客瞅了瞅精度控制,然后依然WA,发现自己在下取整和上取整部分出现问题,于是乎再次瞅了篇博客上、下取整,然后AC,感觉以后碰到浮点数题目谨慎起见,还是都用精度控制吧。

 

下面给出AC代码以及必要的注释:

#include <bits/stdc++.h>

using namespace std;
const int maxn=2000+100;
const double eps=1e-6;

int vis[maxn][maxn];

void solve(int x1,int y1,int x2,int y2)
{
    if(x1==x2||y1==y2) return;

    double k=1.0*(y2-y1)/(x2-x1);
    double y=y1;

    for(int i=x1+1;i<=x2;i++)
    {
        double ny=k+y;//对于单位来说y要增加k个单位
        if(ny-y>eps)
        {
            int yy=floor(y+eps);//相当于对y进行强制转换,注意这里不是四舍五入,标准的四舍五入格式floor(x+0.5),这里要进行精度控制
            while(ny-yy>eps) vis[i][yy++]=1;//精度控制,找一个计数的基本点,每次都以这个格子的左下角为基准点。
        }
        else
        {
            int yy=ceil(y-eps);//同上
            while(yy-ny>eps) vis[i][--yy]=1;//注意这里是--yy因为基准点选在左下角而k有小于零,yy相当于在格子的左上角
        }
        y=ny;//对y进行更新

    }

}

int main()
{
    int n; scanf("%d",&n);

    int x1,y1,x2,y2;
    for(int i=0;i<n;i++)
    {
         scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
         if(x1>x2)
         {
             swap(x1,x2);
             swap(y1,y2);
         }
         solve(x1,y1,x2,y2);
    }

    int ans=0;
    for(int i=0;i<=2000;i++)
    {
        for(int j=0;j<=2000;j++)
        {
            if(vis[i][j]) ans++;
        }
    }

    printf("%d",ans);

    return 0;
}

但是这个代码总感觉怪怪的,因为我们习惯的思维方式都是去根据表达式来去求解,所以下面再给出根据表达式求解的方法(思路一样,只是代码实现的时候略有区别)。

#include <bits/stdc++.h>

using namespace std;
const int maxn=2000+100;
const double eps=1e-6;

int vis[maxn][maxn];

void solve(int x1,int y1,int x2,int y2)
{
    if(x1==x2||y1==y2) return;

    double k=1.0*(y2-y1)/(x2-x1);
    double b=1.0*y1-k*x1;

    for(int i=x1;i<x2;i++)
    {
        double yt1=k*i+b;
        double yt2=k*(i+1)+b;
        if(yt1-yt2>eps)
        {
            swap(yt1,yt2);
        }

        int t1=(int)(yt1+eps);//下取整
        int t2=(int)(yt2+1-eps);//上取整
        for(int j=t1;j<t2;j++) vis[i][j]=1;
    }

}

int main()
{
    int n; scanf("%d",&n);

    int x1,y1,x2,y2;
    for(int i=0;i<n;i++)
    {
         scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
         if(x1>x2)
         {
             swap(x1,x2);
             swap(y1,y2);
         }
         solve(x1,y1,x2,y2);
    }

    int ans=0;
    for(int i=0;i<=2000;i++)
    {
        for(int j=0;j<=2000;j++)
        {
            if(vis[i][j]) ans++;
        }
    }

    printf("%d",ans);

    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值