#每天一到ACM_8.9(1031-围栏)

针对一个由灯照亮的围栏模型,本博客介绍了一个计算围栏各部分光照强度的算法。通过对围栏轮廓的数学建模,利用点、线段等几何元素,实现了光照强度的精确积分计算。

1031-围栏

题目来源北京大学题库

Description

There is an area bounded by a fence on some flat field. The fence has the height h and in the plane projection it has a form of a closed polygonal line (without self-intersections), which is specified by Cartesian coordinates (Xi, Yi) of its N vertices. At the point with coordinates (0, 0) a lamp stands on the field. The lamp may be located either outside or inside the fence, but not on its side as it is shown in the following sample pictures (parts shown in a thin line are not illuminated by the lamp):

The fence is perfectly black, i.e. it is neither reflecting, nor diffusing, nor letting the light through. Research and experiments showed that the following law expresses the intensity of light falling on an arbitrary illuminated point of this fence:
I0=k/r

where k is a known constant value not depending on the point in question, r is the distance between this point and the lamp in the plane projection. The illumination of an infinitesimal narrow vertical board with the width dl and the height h is

  • dI=I0|cosα|dl*h

where I0 is the intensity of light on that board of the fence, α is the angle in the plane projection between the normal to the side of the fence at this point and the direction to the lamp.
You are to write a program that will find the total illumination of the fence that is defined as the sum of illuminations of all its illuminated boards.

Input

The first line of the input file contains the numbers k, h and N, separated by spaces. k and h are real constants. N (3 <= N <= 100) is the number of vertices of the fence. Then N lines follow, every line contains two real numbers Xi and Yi, separated by a space.

Output

Write to the output file the total illumination of the fence rounded to the second digit after the decimal point.

Sample Input

0.5 1.7 3
1.0 3.0
2.0 -1.0
-4.0 -1.0

Sample Output

5.34

题目自行翻译,我也是用了谷歌翻译才看明白的题目。

0.0 分析题目

题目大意就是一个直角坐标系,原点处有一盏灯,灯发出的光会照在篱笆上,对篱笆上的每个点都有不同的光强,与距离成反比,我们要对这个光强进行积分算出照在篱笆上的总光强,公式题目里都有,还有要注意的是光是不能穿过篱笆的所以在这里要注意哪些边界应该计算,哪些不应该。分析题目,大概抽象出了点,线段,及篱笆三个类,这里为了方便都用了结构体来构造。

1.0 ## 点

struct Point
{
    double x;
    double y;
    Point(){ x = 0; y = 0; }
    Point(double xi, double yi)
    {
        x = xi;
        y = yi;
    }
    void set_point(double xi, double yi)
    {
        Point::Point(xi, yi);
    }
    double distance()
    {
        return pow((x*x + y*y), 0.5);
    }
};

Point中有其x,y坐标,以及一个计算到原点(灯)距离的distance函数,没什么好讲的。

1.1 ## 线段

struct Line
{
    double dx;
    double dy;
    double b;
    double lenth;
    Point start;
    Point final;
    Point middle;
    int sign(Point a)
    {
        double t = dx*a.y - dy*a.x + b;
        if (t == 0)
        {
            return 0;
        }
        return t / fabs(t);
    }
public:
    Line(){ dx = 0; dy = 0; b = 0; }
    Line(Point x0, Point x1)
    {
        start = x0;
        final = x1;
        int flag = 1;
        dx = x0.x - x1.x;
        if (dx >= 0)
            flag = 1;
        else
            flag = -1;
        dx = flag*dx;
        dy = flag*(x0.y - x1.y);
        b = dy*x0.x - dx*x0.y;
        lenth = (dx*dx + dy*dy);
        middle.x = b*dy / lenth;
        middle.y = -1 * b*dx / lenth;

    }

    bool is_same_sign(vector<Point> PS)
    {
        int flag = sign(PS[0]);
        int j = 0;
        while (!flag)
            flag = sign(PS[j++]);
        int size = PS.size();
        for (int i = 0; i < size; i++)
        {
            if (flag != sign(PS[i]) && (sign(PS[i]) != 0))
                return 0;
        }
        return 1;
    }
    void display()
    {
        cout << dx << "*y - " << dy << "*x + " << b << " = 0  " << endl;
    }

};

线段比之点就复杂了很多,因为后面为了实现某些功能往里面加了很多东西,不细说,重点说is_same_sign(vector),这个函数是用来判断输入的点集是否在线段的同一侧,而sign()
是用来判断单个点是在线段的哪侧。这样就可以判断一条线是不是整个顶点集最外面的线段(轮廓)了。

1.3 ## 篱笆

struct fence
{
    double k;
    double h;
    double n;
    vector<Point> points;
    vector<Line> lines;
    void set_lines()
    {
        int size = points.size();
        for (int i = 0; i < size - 1; i++)
        {
            for (int j = i + 1; j < size; j++)
            {
                Line here(points[i], points[j]);
                if (here.is_same_sign(points))
                {
                    lines.push_back(here);
                }
            }
        }
    }
    fence(){ k = 0; h = 0; n = 0; }
    fence(string filename)
    {
        int count = 0;
        double temp = 0; Point t;
        ifstream file(filename);
        while (file >> temp)
        {
            if (count == 0)
                k = temp;
            else if (count == 1)
                h = temp;
            else if (count == 2)
                n = temp;
            else
            {
                switch (count%2)
                {
                case 1:t.x = temp; break;
                case 0:t.y = temp; points.push_back(t); break;
                default:
                    break;
                }
            }
            count++;
        }
        set_lines();
        file.close();
    }
    void display()
    {
        printf_s("k = %.2f,h = %.2f,n = %.2f\n", k, h, n);
        for (int i = 0; i < points.size(); i++)
        {
            printf_s("(%.2f,%.2f)\n", points[i].x, points[i].y);
        }
    }
    double sum_a_line(Line a)
    {
        Point here = a.start;
        double count = 0;
        if (a.dx != 0)
        {

            double ki = a.dy / a.dx;
            double step = (a.final.x - a.start.x) / 1000;
            double dl = abs(pow((1 + ki*ki), 0.5)*step);
            for (int i = 0; i < 1000; i++)
            {
                while (count < 0)
                {
                    break;
                }
                double i0 = k / here.distance();
                double dist = here.distance();
                Point middle = a.middle;
                double cosa = abs(a.middle.distance() / here.distance());
                count += i0*dl*h*cosa;
                here.x += step;
                here.y += ki*step;
            }
        }
        else
        {
            double step = (a.final.y - a.start.y) / 1000;
            double dl = a.dy;
            for (int i = 0; i < 1000; i++)
            {
                double i0 = k / here.distance();
                double cosa = abs(a.middle.distance() / here.distance());
                count += i0*dl*h*cosa;
                //here.x += step;
                here.y += step;
            }
        }
        return count;
    }
    double sum_all()
    {
        double count = 0;
        int size = lines.size();
        for (int i = 0; i < size; i++)
        {
            int flag = 1;
            Point middle((lines[i].start.x + lines[i].final.x) / 2, (lines[i].start.y + lines[i].final.y) / 2);
            for (int j = 0; j < size; j++)
            {
                if (i == j)
                    continue;
                if (lines[j].sign(middle) != lines[j].sign(Point(0, 0)))
                {
                    flag = 0;
                    break;
                }
            }
            if (flag == 0)
            {
                continue;
            }
            else
            {
                count += sum_a_line(lines[i]);
            }
        }
        return count;
    }
};

fence,整个题目的核心,另外两个类都是为这个类服务的,工作流程大概是这样的:从输txt文件中读入所需的数据并完成自身所有数据的出始化(获得k,h,n,(x1,y1)~(xn,yn)),用set_lines()函数完成外围轮廓的初始化,sum_a_line(),用来计算一条轮廓的积分,而在sum_all()中通过被遮挡的线上除端点外的任一点肯定与原点分别在任意别的轮廓的两侧这一特点判断该线是否需要计算,最后将未被遮挡的线的计算结果加起来就是结果

1.4 ## 源码

#include<iostream>
#include<fstream>
#include<string>
#include<vector>
#include<iomanip>
using namespace std;

struct Point
{
    double x;
    double y;
    Point(){ x = 0; y = 0; }
    Point(double xi, double yi)
    {
        x = xi;
        y = yi;
    }
    void set_point(double xi, double yi)
    {
        Point::Point(xi, yi);
    }
    double distance()
    {
        return pow((x*x + y*y), 0.5);
    }
};

struct Line
{
    double dx;
    double dy;
    double b;
    double lenth;
    Point start;
    Point final;
    Point middle;
    int sign(Point a)
    {
        double t = dx*a.y - dy*a.x + b;
        if (t == 0)
        {
            return 0;
        }
        return t / fabs(t);
    }
public:
    Line(){ dx = 0; dy = 0; b = 0; }
    Line(Point x0, Point x1)
    {
        start = x0;
        final = x1;
        int flag = 1;
        dx = x0.x - x1.x;
        if (dx >= 0)
            flag = 1;
        else
            flag = -1;
        dx = flag*dx;
        dy = flag*(x0.y - x1.y);
        b = dy*x0.x - dx*x0.y;
        lenth = (dx*dx + dy*dy);
        middle.x = b*dy / lenth;
        middle.y = -1 * b*dx / lenth;

    }

    bool is_same_sign(vector<Point> PS)
    {
        int flag = sign(PS[0]);
        int j = 0;
        while (!flag)
            flag = sign(PS[j++]);
        int size = PS.size();
        for (int i = 0; i < size; i++)
        {
            if (flag != sign(PS[i]) && (sign(PS[i]) != 0))
                return 0;
        }
        return 1;
    }
    void display()
    {
        cout << dx << "*y - " << dy << "*x + " << b << " = 0  " << endl;
    }

};

struct fence
{
    double k;
    double h;
    double n;
    vector<Point> points;
    vector<Line> lines;
    void set_lines()
    {
        int size = points.size();
        for (int i = 0; i < size - 1; i++)
        {
            for (int j = i + 1; j < size; j++)
            {
                Line here(points[i], points[j]);
                if (here.is_same_sign(points))
                {
                    lines.push_back(here);
                }
            }
        }
    }
    fence(){ k = 0; h = 0; n = 0; }
    fence(string filename)
    {
        int count = 0;
        double temp = 0; Point t;
        ifstream file(filename);
        while (file >> temp)
        {
            if (count == 0)
                k = temp;
            else if (count == 1)
                h = temp;
            else if (count == 2)
                n = temp;
            else
            {
                switch (count%2)
                {
                case 1:t.x = temp; break;
                case 0:t.y = temp; points.push_back(t); break;
                default:
                    break;
                }
            }
            count++;
        }
        set_lines();
        file.close();
    }
    void display()
    {
        printf_s("k = %.2f,h = %.2f,n = %.2f\n", k, h, n);
        for (int i = 0; i < points.size(); i++)
        {
            printf_s("(%.2f,%.2f)\n", points[i].x, points[i].y);
        }
    }
    double sum_a_line(Line a)
    {
        Point here = a.start;
        double count = 0;
        if (a.dx != 0)
        {

            double ki = a.dy / a.dx;
            double step = (a.final.x - a.start.x) / 1000;
            double dl = abs(pow((1 + ki*ki), 0.5)*step);
            for (int i = 0; i < 1000; i++)
            {
                while (count < 0)
                {
                    break;
                }
                double i0 = k / here.distance();
                double dist = here.distance();
                Point middle = a.middle;
                double cosa = abs(a.middle.distance() / here.distance());
                count += i0*dl*h*cosa;
                here.x += step;
                here.y += ki*step;
            }
        }
        else
        {
            double step = (a.final.y - a.start.y) / 1000;
            double dl = a.dy;
            for (int i = 0; i < 1000; i++)
            {
                double i0 = k / here.distance();
                double cosa = abs(a.middle.distance() / here.distance());
                count += i0*dl*h*cosa;
                //here.x += step;
                here.y += step;
            }
        }
        return count;
    }
    double sum_all()
    {
        double count = 0;
        int size = lines.size();
        for (int i = 0; i < size; i++)
        {
            int flag = 1;
            Point middle((lines[i].start.x + lines[i].final.x) / 2, (lines[i].start.y + lines[i].final.y) / 2);
            for (int j = 0; j < size; j++)
            {
                if (i == j)
                    continue;
                if (lines[j].sign(middle) != lines[j].sign(Point(0, 0)))
                {
                    flag = 0;
                    break;
                }
            }
            if (flag == 0)
            {
                continue;
            }
            else
            {
                count += sum_a_line(lines[i]);
            }
        }
        return count;
    }
};

int main()
{
    //Point a(2, 3);
    //Point b(4, 5);
    //Line l1(a, b);
    //l1.display();
    //Point x1(0, 0); Point x2(1, 0); Point x3(4, 4); Point x4(0, 2);
    //vector<Point> ps;
    //ps.push_back(x1);
    //ps.push_back(x2);
    //ps.push_back(x3);
    ////ps.push_back(x4);
    //cout<<l1.is_same_sign(ps);
    //ifstream infile("test.txt");
    fence first("in.txt");
    double count = 0;
    //for (int i = 0; i < 3; i++)
    //{
    //  //first.lines[i].display();
    //  cout << first.sum_a_line(first.lines[i]) << endl;
    //  count += first.sum_a_line(first.lines[i]);
    //}
    ////cout << count;
    cout<<first.sum_all();
    ofstream outfile("out.txt",ios::out);
    outfile << std::setprecision(3) << first.sum_all();
    //first.display();
    outfile.close();
    //while (1);
}

2.0 ## 注意事项

输入文件为工程目录下的in.txt,输出为out.txt。
点集中应该不包括原点。

2.1 ## 疑问

事例中有凹多边形但只通过顶点不能确定一个唯一的凹多边形,因此我这里只考虑了凸多边形的情况。
看讨论区似乎用的方法和我的有很大的区别,我并没有尝试将题目化简,这次直接暴力求积分了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值