CSU 1011:Counting Pixels

来源:互联网 发布:阿里云文件上传demo 编辑:程序博客网 时间:2024/06/16 07:47

CSU 1011:Counting Pixels

仅以此博文记录一下今天做这道题时遇到的问题、解决方案和一些收获。希望能给同样遇到问题的人一些帮助。
原题链接
图示

  • 解题思路:

    • 因为原题里说Assume that the entire circle will fit within the area of the display.
      所以这里不考虑圆不能完整显示的情况。那么这道题里的给的X,Y坐标并没有什么用。
      这个圆所覆盖的像素点数只与半径大小有关。
  • 解法一:暴力解法(最容易想到一种想法)
    暴力解法图示
    如图所示可以重新以圆心为原点建立一个直角坐标系,将圆平分成四个部分。
    这样我们只需要计算出其中一个部分所覆盖的像素点的数目(n),那么整个圆所覆盖的像素点数目就是(4 * n)
    这里以第一象限为例子:
    怎么才知道这里面的一个矩形有没有被覆盖呢?
    从图中我们容易看出,对于第一象限的矩形而言,如果这个矩形被包围了,那么这个矩形的左下角的点一直是在圆的内部的。从而我们可以得出数量关系。这个点到原点的距离distance <= r。
    那么我们遍历第一象限里所有左下角满足这个数量关系的矩形,是不是就能找出答案了呢?
    先写个小程序试试看:

#include <stdio.h>int calDistance(int x0, int y0, int x1, int y1);int main(){    int x, y, r;    int initialRadius;//半径    int count;//统计有多少个满足条件的点    int distance ;//点到原点的距离    int j;    while (scanf("%d %d %d",&x,&y,&r)&&(x||y||r))//根据原题要求当输入0 0 0时结束程序    {        count = 0;        j = 0;        initialRadius = r;    //以下两个循环用作从上往下遍历所有满足左下角的点到原点距离<=半径点。        for (r; r -1 >= 0;r--)        {            for (j = 0;j < initialRadius;j++)            {                distance = calDistance(j, r - 1, 0, 0);                if (distance <= initialRadius * initialRadius  )                {                    printf("(%d,%d)\n", j,r - 1);//我把所有满足条件的点列出来给你们看                    count++;                }            }        }        printf("%d\n", 4 * count);    }    return 0;}int calDistance(int x0, int y0, int x1, int y1){    return (x0*x0 - x1*x1) + (y0*y0 - y1*y1);}

程序运行的结果如图所示test1
但是正确答案应该是88。很明显我们算多了两个点,但是这两个点到底是哪两个点呢?
我们重新来看我来找满足条件的矩形的方法时,我们会发现有一些漏洞。如暴力解法图示中的红色圆框中点:(3,4)和(4,3)。它们虽然也满足distance <= initialRadius * initialRadius
但很明显它们所在的矩形框不满足原题条件pixels whose edge or corner are just touched by the circle, however, are not lit. 顶角或边框刚好被碰到的像素点是不应该被算入的。
于是我们应改进一下代码:把满足到原点的距离等于半径的平方,并且不再x轴或者Y轴上的点删掉。

#include <stdio.h>int calDistance(int x0, int y0, int x1, int y1);int main(){    int x, y, r;    int initialRadius;//半径    int count;//统计有多少个满足条件的点    int distance ;//点到原点的距离    int j;    while (scanf("%d %d %d",&x,&y,&r)&&(x||y||r))//根据原题要求当输入0 0 0时结束程序    {        count = 0;        j = 0;        initialRadius = r;        for (r; r -1 >= 0;r--)//计算左下角到原点距离        {            for (j = 0;j < initialRadius;j++)            {                distance = calDistance(j, r - 1, 0, 0);                if (distance <= initialRadius * initialRadius  )                {                    printf("%d,%d", &j, &r - 1);                    count++;                }                if (distance == initialRadius * initialRadius && r - 1 >= 0)                {                    count--;                }            }        }        printf("%d\n", 4 * count);    }    return 0;}int calDistance(int x0, int y0, int x1, int y1){    return (x0*x0 - x1*x1) + (y0*y0 - y1*y1);}

运行一下结果:
test2

哈哈,得出正确答案了。那么你就就欢天喜地地跑去OJ提交答案了?
然而OJ会告诉你:Time Limit Exceed.
WTF
什么!我好不容易想出来的想法居然TLE了。你在逗我吗!!!
想想也是,我们还是太年轻了。
这个做法忽略了一个题目中很重要的地方就是:r(1≤x,y,r≤1,000,000)
r可以是一个非常非常大的数啊!你可以用上面写好的那个程序输入r为1e6没有个10多秒都跑不出来啊!(土豪电脑无视我。。。)。想想也是,上面那个程序当r=1e6的时候,你想想上面的程序得计算多少个点的平方去比较。(有兴趣的可以考虑一下这个算法的时间复杂度)。你想想如果你画个圆,电脑要延迟好久才显示出来,这你能忍?

  • 技巧解法
    这里写图片描述
    我们重新看这幅图,可以找到另外一个数量关系。
    我们可以将第一象限的像素点组分成r排,其中第i排(i = 0,1,2…r)的矩形个数等于第i排直线高度再向上取整。(参考绿色那条线比较容易看出,第3排有5个矩形,绿色线的高度大约是4.5)。
    我们可以通过勾股定理求出每一条竖线的高度,就可以得出每排矩形的个数,最后加起来即可。(这个方法只需要计算r次勾股定理)
    用代码来表示就是
#include <stdio.h>#include <math.h>int main(){    int x, y, r;    int count;    int i;    while (scanf("%d %d %d", &x, &y, &r) != EOF && (x | y | r))    {        count = 0;        for (i = 0; i < r; i++)        {            count += ceil(sqrt((double)((r*r) - (i*i))));        }///ceil()是向上取整函数        printf("%d\n", 4 * count);    }    return 0;}

跑出来的结果是一样的test2
然后你又欢天喜地地跑去OJ提交代码了??
然而很遗憾,OJ还是会告诉你Time Limit Exceed.
WTF2
你在逗我吗?说了个技巧方法还以为有多厉害,还不是过不了!!!破东西!!!
(怪我咯)
都说了要注意变量的范围啊。你需要把变量类型改成long long

#include <stdio.h>#include <math.h>int main(){    long long x, y, r;    long long count;    long long i;    while (scanf("%lld %lld %lld", &x, &y, &r) != EOF && (x | y | r))    {        count = 0;        for (i = 0; i < r; i++)        {            count += (long long)ceil(sqrt((double)((r*r) - (i*i))));        }        printf("%lld\n", 4 * count);    }    return 0;}

这样子,你再去提交。你会发现,你会讶异:
这里写图片描述
这这这终于绿了!也是心累啊!!

  • 总结:
    1. 好想不好做,好做不好想。做一件事可以有很多的解决方案,但是如何找到一种最优的算法,我想这就是研究ACM的魅力所在了吧。
    2. 注意题目中的细节。如本题中的圆能不能完整显示,变量的范围有没有超出变量类型的范围等等都是需要考虑的。
    3. 以上就是我今天做这道题的时候遇到的坑,自己的解决方法和心得体会。本人只是刚刚踏上ACM道路的菜鸟一枚,本文存在纰漏之处愿大家指教,虚心学习更多更好的方法。
0 0