• 2017年8月9日 计算几何 知识点


    2017年8月9日 计算几何 知识点

    张炳琪

    一.知识点相关

     几道有关几何的题目,杂题

    二.今天完成8道题目

    1.洛谷P2181 对角线                               入门难度                                          几何推论

    2.洛谷P1355 神秘大三角                        普及/提高-                                        几何推论

    3.洛谷P1429 平面最近点对(加强版)   提高+省选-                                       几何分治

    4.洛谷P2735 电网 Electric Fences            普及/提高-                                        几何推论

    5.洛谷 P2726 阶乘 Factorials                  普及提高-                                         模拟

    6.洛谷 P2725 邮票 Stamps                       普及/提高-                                        动态规划

    7.洛谷P2732 商店购物 Shopping Offers   提高+省选-                                       动态规划

    8.洛谷 P2731 Riding the Fences                 普及+提高                                        欧拉路

    三、不会的题目是

    四、以下贴作业的题目、解析及代码(要有序列号)

    1. P2181 对角线

    题目描述

    对于一个N个定点的凸多边形,他的任何三条对角线都不会交于一点。请求楚图形中对角线交点的个数。

    例如,6边形:

    输入输出格式

    输入格式:

    第一行一个n,代表边数。

    输出格式:

    第一行输出交点数量

    输入输出样例

    输入样例#1:

    3

    输出样例#1:

    0

    输入样例#2:

    6

    输出样例#2:

    15

    说明

    50%的测试数据 3≤N≤100;

    100%的测试数据 3≤N≤100000.

    方法::/*

    这属于计算几何中的推论公式吧,任何凸n边形中对角线焦点个数

    等于(n)*(n - 1)*(n - 2)*(n - 3) / 24; 也就是C n-4 n

    */

    代码::

    #include<cstdio>

    #include<algorithm>

    #include<iostream>

    using namespace std;

    int main()

    {

           unsigned long long  n;

           cin >> n;

           cout<< n * (n - 1) / 2 * (n - 2) / 3 *(n - 3)/ 4 <<endl;

           return 0;

    }

    P1355 神秘大三角

    题目描述

    判断一个点与已知三角形的位置关系。

    输入输出格式

    输入格式:

    前三行:每行一个坐标,表示该三角形的三个顶点

    第四行:一个点的坐标,试判断该点与前三个点围成三角形的位置关系

    (详见样例)

    所有坐标值均为整数。

    输出格式:

    若点在三角形内(不含边界),输出1;

    若点在三角形外(不含边界),输出2;

    若点在三角形边界上(不含顶点),输出3;

    若点在三角形顶点上,输出4。

    输入输出样例

    输入样例#1:

    (0,0)

    (3,0)

    (0,3)

    (1,1)

    输出样例#1:

    1

    说明

    【数据规模与约定】

    对于100%数据,0<=所有点的横、纵坐标<=100

    方法::

    /*

    判断一个点和给定三角形的 位置关系  可以先判断是否在三个顶点上g

    如果不是的话  将这个点分别和另外三个点连边,然后计算连边后

    得到的三个小三角形的面积和,若等于原三角形面积和  则在三角形内部

    如果某个三角形面积是0  则在三角形边上否则就在三角形外部

    */

    代码::#include<cstdio>

    #include<algorithm>

    #include<cmath>

    #include<iostream>

    using namespace std;

    double notex[123],notey[123];

    double qiu(int a,int b)

    {

           return sqrt((notex[a] - notex[b]) * (notex[a] - notex[b]) + (notey[a] - notey[b]) * (notey[a] - notey[b]));

    }

    double work(int a,int b,int c)

    {

           double aa = qiu(a,b);

           double bb = qiu(b,c);

           double cc = qiu(a,c);

           double p = (aa + bb + cc) / 2.0;

           double ans = sqrt(p * (p - aa) * (p - bb) * (p - cc));

           return (int) (ans * 100 + 0.5);

     }

    int main()

    {

           for(int i = 1;i <= 4;i++)

           {

                  char c;

                  cin >> c >> notex[i] >> c >> notey[i] >> c;

                  //scanf("(%lf,%lf)",&notex[i],&notey[i]);

           }    

           if((notex[4] == notex[1] && notey[4] == notey[1]) ||

           (notex[4] == notex[2] && notey[4] == notey[2]) ||

           (notex[4] == notex[3] && notey[4] == notey[3]) )

           {

                  printf("4");

                  return 0;

           }

           int yuan = work(1,2,3);

           int s1 = work(1,3,4);

           int s2 = work(1,2,4);

           int s3 = work(2,3,4);

          

           if(s1 + s2 + s3 > yuan)

           {

                  printf("2");

                  return 0;

           }    

          

           if(s1 == 0 || s2 == 0 || s3 == 0)

                  printf("3");

           else

                  printf("1");

           return 0;

    }

    P1429 平面最近点对(加强版)

    题目描述

    给定平面上n个点,找出其中的一对点的距离,使得在这n个点的所有点对中,该距离为所有点对中最小的

    输入输出格式

    输入格式:

    第一行:n;2≤n≤200000

    接下来n行:每行两个实数:x y,表示一个点的行坐标和列坐标,中间用一个空格隔开。

    输出格式:

    仅一行,一个实数,表示最短距离,精确到小数点后面4位。

    输入输出样例

    输入样例#1:

    3

    1 1

    1 2

    2 2

    输出样例#1:

    1.0000

    说明

    0<=x,y<=10^9

    方法::

    //平面  最近 点对  的做法 是将所有的点  按照 一个坐标 二分  ,之后再二分等等   ,

    对于每一块中 我们可以求出最近点对的距离(二分成一个点时,距离返回无限大)

    比较难的操作 是将 原本的分开的点合并的方法   朴素的方法没什么优势   我们能够首先扫一遍

    将与对称轴距离已经超过答案的 点 去掉   ,然后将剩余的点  按照 y的顺序排序,两个循环合并

    在不满足某些情况的时候  可以提前跳出  节省时间  而经过大佬的推算   每次合并的点的个数不会超过六对

    代码::

    #include<cstdio>

    #include<algorithm>

    #include<iostream>

    #include<cmath>

    #define MAXN 200001

    #define INF 0x7ffffff

    using namespace std;

    struct Note

    {

        double x;

        double y;

    }note[MAXN];

    int n;

    int stack[MAXN];

    inline double MIN(double a, double b)

    {

        return a < b?a : b;

    }

    inline double ABS(double a)

    {

        return a > 0?a:-a;

    }

    bool cmp(Note a, Note b)

    {

        return a.x == b.x?a.y < b.y:a.x < b.x;

    }

    bool recmp(int a, int b)

    {

        return note[a].y < note[b].y;

    }

    inline double ask(int a,int b)

    {

        return sqrt((note[a].x - note[b].x) * (note[a].x - note[b].x) + (note[a].y - note[b].y) * (note[a].y - note[b].y));

    }

    double unionn(int l, int r)

    {

        if(l == r)

        {

            return INF;

        }

        int top = 0;

        int mid = (l + r) >> 1;

        double ans = MIN(unionn(l,mid),unionn(mid + 1,r));

        for(int i = l; i <= r; i++)

        {

            if(ABS(note[i].x - note[mid].x) <= ans)

            {

                stack[++top] = i;

            }

        }

        sort(stack + 1, stack + top + 1,recmp);

        for(int i = 1; i <= top; i++)

        {

            for(int j = i + 1; j <= top && note[stack[j]].y - note[stack[i]].y < ans;j ++)

            {

                double op = ask(stack[i],stack[j]);

                if(ans > op)ans = op;

            }

        }

        return ans;

    }

    int main()

    {

        scanf("%d",&n);

        for(int i = 1; i <= n; i ++)

        {

            scanf("%lf%lf",&note[i].x,&note[i].y);

        }

        sort(note + 1,note + n + 1,cmp);

       

        printf("%.4lf",unionn(1,n));

       

        return 0;

    }

    P2735 电网 Electric Fences

    题目描述

    在本题中,格点是指横纵坐标皆为整数的点。

    为了圈养他的牛,农夫约翰(Farmer John)建造了一个三角形的电网。他从原点(0,0)牵出一根通电的电线,连接格点(n,m)(0<=n<32000,0<m<32000),再连接格点(p,0)(p>0),最后回到原点。

    牛可以在不碰到电网的情况下被放到电网内部的每一个格点上(十分瘦的牛)。如果一个格点碰到了电网,牛绝对不可以被放到该格点之上(或许Farmer John会有一些收获)。那么有多少头牛可以被放到农夫约翰的电网中去呢?

    输入输出格式

    输入格式:

    输入文件只有一行,包含三个用空格隔开的整数:n,m和p。

    输出格式:

    输出文件只有一行,包含一个整数,代表能被指定的电网包含的牛的数目。

    输入输出样例

    输入样例#1:

    7 5 10

    输出样例#1:

    20

    说明

    题目翻译来自NOCOW。

    USACO Training Section 3.4

    方法::

    /*

    首先这个题目要依靠的一个定理叫做皮克定理  原版的皮克定理用于 求格点中的多边形

    的面积 公式可以表示为2S=2a+b-2,其中a表示多边形内部的点数,b表示多边

    形边界上的点数,s表示多边形的面积。

    然后这里用到的推论是   三角形斜边经过的个点  等于两直角边 长度的最大公约数

    我们用普通方法求出面积  在用以上方法求出边上的点   就能求出内部的点了

    */

    代码::

    #include<cstdio>

    #include<algorithm>

    using namespace std;

    int gcd(int a,int b)

    {

           return b == 0 ? a : gcd(b,a % b);

    }

    int main()

    {

           int n,m,p;

           scanf("%d%d%d",&n,&m,&p);

           int s = p * m / 2;   

           int bian = (gcd(n,m) + gcd(abs(p - n),m) + p);

           printf("%d",s - bian / 2 + 1);

           return 0;

    }

    P2726 阶乘 Factorials

    题目背景

    N的阶乘写作N!,表示小于等于N的所有正整数的乘积。

    题目描述

    阶乘会变大得很快,如13!就必须用32位整数类型来存储,到了70!即使用浮点数也存不下了。 你的任务是找到阶乘最前面的非零位。举个例子:

    5!=1*2*3*4*5=120,所以5!的最靠后的非零位是2。

    7!=1*2*3*4*5*6*7=5040,所以最靠后的非零位是4。

    输入输出格式

    输入格式:

    共一行,一个不大于4,220的正整数N

    输出格式:

    共一行,输出N!最靠后的非零位。

    输入输出样例

    输入样例#1:

    7

    输出样例#1:

    4

    说明

    题目翻译来自NOCOW。

    USACO Training Section 3.2

    方法::

    /*

    模拟进行操作  每次求完不断除以十直到后面没有0  然后再模10

    */

    代码::

    #include<cstdio>

    #include<algorithm>

    using namespace std;

    int main()

    {

           int zz = 1;

           int n;

           scanf("%d",&n);

           for(int i = 1;i <= n;i++)

           {

                  zz *= i;

                  while(zz % 10 == 0)zz /= 10;

                  zz %= 1000;

           }

           printf("%d",zz % 10);

           return 0;

     }

     P2725 邮票 Stamps

    题目背景

    给一组 N 枚邮票的面值集合(如,{1 分,3 分})和一个上限 K —— 表示信封上能够贴 K 张邮票。计算从 1 到 M 的最大连续可贴出的邮资。

    题目描述

    例如,假设有 1 分和 3 分的邮票;你最多可以贴 5 张邮票。很容易贴出 1 到 5 分的邮资(用 1 分邮票贴就行了),接下来的邮资也不难:

    6 = 3 + 3

    7 = 3 + 3 + 1

    8 = 3 + 3 + 1 + 1

    9 = 3 + 3 + 3

    10 = 3 + 3 + 3 + 1

    11 = 3 + 3 + 3 + 1 + 1

    12 = 3 + 3 + 3 + 3

    13 = 3 + 3 + 3 + 3 + 1

    然而,使用 5 枚 1 分或者 3 分的邮票根本不可能贴出 14 分的邮资。因此,对于这两种邮票的集合和上限 K=5,答案是 M=13。 [规模最大的一个点的时限是3s]

    小提示:因为14贴不出来,所以最高上限是13而不是15

    输入输出格式

    输入格式:

    第 1 行: 两个整数,K 和 N。K(1 <= K <= 200)是可用的邮票总数。N(1 <= N <= 50)是邮票面值的数量。

    第 2 行 .. 文件末: N 个整数,每行 15 个,列出所有的 N 个邮票的面值,每张邮票的面值不超过 10000。

    输出格式:

    第 1 行:一个整数,从 1 分开始连续的可用集合中不多于 K 张邮票贴出的邮资数。

    输入输出样例

    输入样例#1:

    5 2

    1 3

    输出样例#1:

    13

    说明

    题目翻译来自NOCOW。

    USACO Training Section 3.1

    /*

    这个题目可以用背包来做  做法是记录当前状态的最少需求

    转移的时候求min即可  注意的是当只有 在要求的件数之内才能用

    */

    代码::

    #include<cstdio>

    #include<algorithm>

    using namespace std;

    int dp[7234567];

    int main()

    {

           int k,n;

           scanf("%d%d",&k,&n);

           for(int i = 1;i <= 2230000;i++)

           {

                  dp[i] = 0x7ffffff;

           }

           for(int i = 1;i <= n;i++)

           {

                  int op;

                  scanf("%d",&op);

                  for(int j = op;j <= 2230000;j++)

                  {

                         if(dp[j - op] < k)

                         {

                                dp[j] = min(dp[j],dp[j - op] + 1);

                         }

                  }

           }

           for(int i = 1;i <= 2230000;i++)

                  if(dp[i] == 0x7ffffff)

                  {

                         printf("%d",i - 1);

                         return 0;

                  }

    }

    P2732 商店购物 Shopping Offers

    题目背景

    在商店中,每一种商品都有一个价格(用整数表示)。例如,一朵花的价格是 2 zorkmids (z),而一个花瓶的价格是 5z 。为了吸引更多的顾客,商店举行了促销活动。

    题目描述

    促销活动把一个或多个商品组合起来降价销售,例如:

    三朵花的价格是 5z 而不是 6z, 两个花瓶和一朵花的价格是 10z 而不是 12z。 编写一个程序,计算顾客购买一定商品的花费,尽量利用优惠使花费最少。尽管有时候添加其他商品可以获得更少的花费,但是你不能这么做。

    对于上面的商品信息,购买三朵花和两个花瓶的最少花费的方案是:以优惠价购买两个花瓶和一朵花(10z),以原价购买两朵花(4z)。

    输入输出格式

    输入格式:

    输入文件包括一些商店提供的优惠信息,接着是购物清单。(最多有5种商品)

    第一行 优惠方案的种类数(0 <= s <= 99)。

    第二行..第s+1 行 每一行都用几个整数来表示一种优惠方式。第一个整数 n (1 <= n <= 5),表示这种优惠方式由 n 种商品组成。后面 n 对整数 c 和 k 表示 k (1 <= k <= 5)个编号为 c (1 <= c <= 999)的商品共同构成这种优惠,最后的整数 p 表示这种优惠的优惠价(1 <= p <= 9999)。优惠价总是比原价低。

    第 s+2 行 这一行有一个整数 b (0 <= b <= 5),表示需要购买 b 种不同的商品。

    第 s+3 行..第 s+b+2 行 这 b 行中的每一行包括三个整数:c,k,p。 c 表示唯一的商品编号(1 <= c <= 999),k 表示需要购买的 c 商品的数量(1 <= k <= 5)。p 表示 c 商品的原价(1 <= p <= 999)。最多购买 5*5=25 个商品。

    输出格式:

    只有一行,输出一个整数:购买这些物品的最低价格。

    输入输出样例

    输入样例#1:

    2

    1 7 3 5

    2 7 1 8 2 10

    2

    7 3 2

    8 2 5

    输出样例#1:

    14

    方法::

    /*

    完全背包问题   将每一个组合单独看成一种物品   最后的单个的也看做一种物品

    然后 进行完全背包的dp就可以了

    */

    代码::

    #include<cstdio>

    #include<algorithm>

    #include<iostream>

    #include<cstring>

    using namespace std;

    struct Note{

           int num[6];

           int cost;

    }note[1234];

    int dp[6][6][6][6][6];

    int ID[1001];

    int idnum = 0;

    int need[11];

    int main()

    {

           int s;

           scanf("%d",&s);

           for(int i = 1;i <= s;i++)

           {

                  int n;

                  scanf("%d",&n);

                  for(int j = 1;j <= n;j++)

                  {

                         int idid,ver;

                         scanf("%d%d",&idid,&ver);

                         if(ID[idid] == 0)

                         {

                                ID[idid] = ++idnum;

                         }

                         note[i].num[ID[idid]] = ver;

                  }

                  scanf("%d",&note[i].cost);

           }

           int op;

           scanf("%d",&op);

          

           memset(dp,0x3e,sizeof(dp));

           dp[0][0][0][0][0] = 0;

          

           for(int i = 1;i <= op;i++)

           {

                  s++;

                  int ci,bi,veri;

                  scanf("%d%d%d",&ci,&bi,&veri);

                  if(ID[ci] == 0)

                  {

                         ID[ci] = ++idnum;

                  }

                  note[s].num[ID[ci]] = 1;

                  note[s].cost = veri;

                  need[ID[ci]] = bi;

           }

          

           for(int o = 1;o <= s;o++)

                  for(int v1 = note[o].num[1];v1 <= need[1];v1++)

                         for(int v2 = note[o].num[2];v2 <= need[2];v2++)

                                for(int v3 = note[o].num[3];v3 <= need[3];v3++)

                                       for(int v4 = note[o].num[4];v4 <= need[4];v4++)

                                              for(int v5 = note[o].num[5];v5 <= need[5];v5++)

                                              {

                                                     dp[v1][v2][v3][v4][v5] = min(dp[v1][v2][v3][v4][v5],

                                                     dp[v1 - note[o].num[1]]

                                                     [v2 - note[o].num[2]]

                                                     [v3 - note[o].num[3]]

                                                     [v4 - note[o].num[4]][v5 - note[o].num[5]] + note[o].cost);                          

                                              }

          

           printf("%d",dp[need[1]][need[2]][need[3]][need[4]][need[5]]);

           return 0;

    }

     P2731 骑马修栅栏 Riding the Fences

    题目背景

    Farmer John每年有很多栅栏要修理。他总是骑着马穿过每一个栅栏并修复它破损的地方。

    题目描述

    John是一个与其他农民一样懒的人。他讨厌骑马,因此从来不两次经过一个栅栏。你必须编一个程序,读入栅栏网络的描述,并计算出一条修栅栏的路径,使每个栅栏都恰好被经过一次。John能从任何一个顶点(即两个栅栏的交点)开始骑马,在任意一个顶点结束。

    每一个栅栏连接两个顶点,顶点用1到500标号(虽然有的农场并没有500个顶点)。一个顶点上可连接任意多(>=1)个栅栏。两顶点间可能有多个栅栏。所有栅栏都是连通的(也就是你可以从任意一个栅栏到达另外的所有栅栏)。

    你的程序必须输出骑马的路径(用路上依次经过的顶点号码表示)。我们如果把输出的路径看成是一个500进制的数,那么当存在多组解的情况下,输出500进制表示法中最小的一个 (也就是输出第一位较小的,如果还有多组解,输出第二位较小的,等等)。

    输入数据保证至少有一个解。

    输入输出格式

    输入格式:

    第1行: 一个整数F(1 <= F <= 1024),表示栅栏的数目

    第2到F+1行: 每行两个整数i, j(1 <= i,j <= 500)表示这条栅栏连接i与j号顶点。

    输出格式:

    输出应当有F+1行,每行一个整数,依次表示路径经过的顶点号。注意数据可能有多组解,但是只有上面题目要求的那一组解是认为正确的。

    输入输出样例

    输入样例#1:

    9

    1 2

    2 3

    3 4

    4 2

    4 5

    2 5

    5 6

    5 7

    4 6

    输出样例#1:

    1

    2

    3

    4

    2

    5

    4

    6

    5

    7

    说明

    题目翻译来自NOCOW。

    USACO Training Section 3.3

    方法::

    /*

    这个题目应该也是比较简单  输入的边恰好能够组成欧拉回路

    为了满足题意中的 最小的路径 我们用邻接矩阵来存边  并记录每个点的连边情况

    按照一般求欧拉回路的套路来做即可

    */

    代码::

    #include<cstdio>

    #include<algorithm>

    #define MAXN 501

    using namespace std;

    int m[MAXN][MAXN];

    int note[MAXN];

    int path[12345];

    int top = 0;

    void dfs(int now)

    {

           for(int i = 1;i <= 500;i++)

           {

                  if(m[now][i])

                  {

                         m[now][i] --;

                         m[i][now] --;

                         dfs(i);

                  }

           }

           path[++top] = now;

           //path[++top] = now + 1;

     }

    int main()

    {

           int n;

           scanf("%d",&n);

           while(n--)

           {

                  int vi,vj;

                  scanf("%d%d",&vi,&vj);

                  m[vi][vj]++;

                  m[vj][vi]++;

                  note[vi]++;

                  note[vj]++;

           }

                 

           for(int i = 1;i <= 500;i++)

           {

                  if(note[i] & 1)

                  {

                         dfs(i);

                         break;

                  }

           }

           for(int i = 1,j = 1;i <= 500;i++)

           {

                  if(i + j != j + i)

                         printf("zbq is handsome ");

           }

           if(top == 0)

                  dfs(1);

           for(int i = top;i >= 1;i--)

                  printf("%d ",path[i]);

          

           return 0;

    }

  • 相关阅读:
    Ignoring HTTPS certificates
    利用Httponly提升web应用程序安全性
    HttpUrlConnection java.net.SocketException: Software caused connection abort: recv failed
    DISPOSE_ON_CLOSE 和 EXIT_ON_CLOSE 的区别
    Swing多线程
    攒机知识积累
    数组最大子数组和
    fork()详解
    理解Socket编程【转载】
    STM32F407_LED代码
  • 原文地址:https://www.cnblogs.com/luoyibujue/p/7327677.html
Copyright © 2020-2023  润新知