HDU 1665 - Different Digits(几何 + 欧拉定理)

题意:在一个平面上,给定一个由 n 个点(4 <= n <= 300)组成的一封闭笔画图形(第一个端点与第 n 个端点重合),求这个图形将平面分成几个部分。

需要用到欧拉定理;

欧拉定理:设图的顶点数为 v ,边数(三维中为棱的个数)为 e ,面数为 f ,则v + f - e = 2.

则若求面数,只要求出顶点数 v 和边数 e,即能得出答案 f = e + 2 - v.

(注意:这里的边数 e,是指的单独一条线段,若中间被点分隔开,则算作多条线段而非一条)

具体处理如下:

1、先枚举任意两条画出的线段,看是否产生了新的交点,产生了即存入新的点数组中;

2、将产生的交点去重(可能出现三条及以上直线交于一点的情况);

3、边数 e 初始必为 n - 1(因为起点和终点重合),然后再统计新的交点断开产生的新边;

4、根据顶点数和边数算出答案即可.

套用几何模板,代码如下(部分注释注明了函数功能):

#pragma comment(linker, "/STACK:102400000, 102400000")
#include<cstdio>
#include<cstring>
#include<cctype>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<sstream>
#include<iterator>
#include<algorithm>
#include<string>
#include<vector>
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<list>
#define fin freopen("in.txt", "r", stdin)
#define fout freopen("out.txt", "w", stdout)
#define pr(x) cout << #x << " : " << x << "   "
#define prln(x) cout << #x << " : " << x << endl
#define Min(a, b) a < b ? a : b
#define Max(a, b) a < b ? b : a
typedef long long ll;
typedef unsigned long long llu;
const int INT_INF = 0x3f3f3f3f;
const int INT_M_INF = 0x7f7f7f7f;
const ll LL_INF = 0x3f3f3f3f3f3f3f3f;
const ll LL_M_INF = 0x7f7f7f7f7f7f7f7f;
const double pi = acos(-1.0);
const int dr[] = {0, 0, -1, 1, -1, -1, 1, 1};
const int dc[] = {-1, 1, 0, 0, -1, 1, -1, 1};
const int MOD = 10000;
using namespace std;

#define NDEBUG
#include<cassert>
const int MAXN = 300 + 10;
const int MAXT = 10000 + 10;

struct Point{
    double x, y;
    Point(double x = 0, double y = 0) : x(x), y(y) {}
};

typedef Point Vector;

Vector operator + (Vector A, Vector B) {return Vector(A.x + B.x, A.y + B.y);}
Vector operator - (Point A, Point B) {return Vector(A.x - B.x, A.y - B.y);}
Vector operator * (Vector A, double p) {return Vector(A.x * p, A.y * p);}
Vector operator / (Vector A, double p) {return Vector(A.x / p, A.y / p);}

bool operator < (const Point &a, const Point &b){
    return a.x < b.x || (a.x == b.x && a.y < b.y);
}

const double EPS = 1e-8;

int dcmp(double x){
    if(fabs(x) < EPS)  return 0;
    else  return x < 0 ? -1 : 1;
}

bool operator == (const Point &a, const Point &b){
    return dcmp(a.x - b.x) == 0 && dcmp(a.y - b.y) == 0;
}

double Dot(Vector A, Vector B) {return A.x * B.x + A.y * B.y;}
double Cross(Vector A, Vector B) {return A.x * B.y - B.x * A.y;}

//求两条线段的交点,第一条线段的起点为P,向量为V,第二条为Q和w(调用前保证有交点)
Point GetLineIntersection(Point P, Vector v, Point Q, Vector w){
    Vector u = P - Q;
    double t = Cross(w, u) / Cross(v, w);
    return P + v * t;
}

//判断a1a2与b1b2两条线段是否有交点
bool SegmentProperIntersection(Point &a1, Point &a2, Point &b1, Point &b2){
    double c1 = Cross(a2 - a1, b1 - a1);
    double c2 = Cross(a2 - a1, b2 - a1);
    double c3 = Cross(b2 - b1, a1 - b1);
    double c4 = Cross(b2 - b1, a2 - b1);
    return dcmp(c1) * dcmp(c2) < 0 && dcmp(c3) * dcmp(c4) < 0;
}

//判断p是否在a1a2线段上(端点处不算)
bool OnSegment(Point p, Point a1, Point a2){
    return dcmp(Cross(a1 - p, a2 - p)) == 0 && dcmp(Dot(a1 - p, a2 - p)) < 0;
}

Point p[MAXN];  //输入的点
Point v[MAXN * MAXN];  //输入的点,以及线段相交出的点

//欧拉定理:平面图的顶点数为 v ,边数(三维中为棱的个数)为 e ,面数为 f ,则v + f - e = 2
//答案为 f = e + 2 - v

int main(){
    int n, kase = 0;
    while(scanf("%d", &n) == 1 && n){
        for(int i = 0; i < n; ++i){
            scanf("%lf%lf", &p[i].x, &p[i].y);
            v[i] = p[i];
        }
        int c = n - 1, e = n - 1;
        //枚举每两条线段,检查是否相交,若相交则加入 c 数组
        for(int i = 0; i < n - 1; ++i)
            for(int j = i + 1; j < n - 1; ++j)
                if(SegmentProperIntersection(p[i], p[i + 1], p[j], p[j + 1]))
                    v[c++] = GetLineIntersection(p[i], p[i + 1] - p[i], p[j], p[j + 1] - p[j]);

        //去除重复相交的顶点
        sort(v, v + c);
        c = unique(v, v + c) - v;

        //若新加入的点分割了原来的线段,则边数增加
        for(int i = 0; i < c; ++i)
            for(int j = 0; j < n - 1; ++j)
                if(OnSegment(v[i], p[j], p[j + 1]))  ++e;
        printf("Case %d: There are %d pieces.\n", ++kase, e + 2 - c);
    }
    return 0;
}
时间: 2024-10-14 14:35:22

HDU 1665 - Different Digits(几何 + 欧拉定理)的相关文章

HDU 4333 Revolving Digits 扩展KMP

链接:http://acm.hdu.edu.cn/showproblem.php?pid=4333 题意:给以数字字符串,移动最后若干位到最前边,统计得到的数字有多少比原来大,有多少和原来相同,有多少比原来的小. 思路:拓展KMP中的next数组标记的是子串和母串的公共前缀的长度,要将字符串长度变成原来二倍,这样如果变换后不是完全相同的数字也即公共前缀长度大于等于字符串长度,那么字母串公共前缀的下一位的大小比较就是题目所要求的比较.由于相同的数字串只算一次,则只要统计比较第一个"循环节"

HDU 4793 Collision + HDU 4798 Skycity 简单几何

HDU 4793 链接:http://acm.hdu.edu.cn/showproblem.php?pid=4793 题意:给一个以(0,0)为圆心半径为R的圆形区域,中间放着一个(0,0)为圆心半径为Rm的圆盘,在坐标(x,y)处(严格在圆形区域外)放着一枚半径为r的硬币,运动方向和速度为(vx,vy),在运动中碰到圆盘时,会按碰撞问题反弹(圆盘是固定不动的),问硬币会在圆形区域里呆多长时间(硬币只要有一点点在圆形区域里就记为硬币在圆形区域内). 思路:首先先计算出硬币在移动过程中如果不与圆盘

扩展KMP,附上例题(HDU - 4333 Revolving Digits)

给出模板串S和串T,长度分别为Slen和Tlen,在线性时间内,对于每个S[i](0<=i<Slen),求出S[i..Slen-1]与T的 最长公共前缀长度,记为extend[i],extend[i]存放s[i]开始与T的最长公共前缀长度. 例子 a a a a a a a b b b a a a a a c extend 5 4 3 2 1 0 0 0 0 0 HDU - 4333 Revolving Digits Time Limit: 3000/1000 MS (Java/Others)

扩展KMP - HDU 4333 Revolving Digits

Revolving Digits Problem's Link: http://acm.hdu.edu.cn/showproblem.php?pid=4333 Mean: 给你一个字符串,你可以将该字符串的任意长度后缀截取下来然后接到最前面,让你统计所有新串中有多少种字典序小于.等于.大于原串. analyse: KMP的经典题. 首先我们将原串扩展成两倍,算一遍扩展KMP(自匹配),时间复杂度O(n). 这样一来,我们就得到了eKMP[i],eKMP[i]代表s[i...len-1]与s的最长

hdu 4333 Revolving Digits

Revolving Digits http://acm.hdu.edu.cn/showproblem.php?pid=4333 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Problem Description One day Silence is interested in revolving the digits of a positive integer. In th

第六场 hdu 6097 Mindis (几何)

http://acm.hdu.edu.cn/showproblem.php?pid=6097 题目大意:有个圆,圆内有两个点P,Q,已知PO=QO,求圆上一点D,使得PD+QD最小 解题思路:官方题解 找着题解一步步想的,代码中有详细的解释 AC代码: 1 #include <iostream> 2 #include <bits/stdc++.h> 3 using namespace std; 4 const double eps=1e-8; 5 void work() 6 { 7

hdu 1664 Different Digits, spoj 3929 , 同余,bfs

Time Limit: 10000/4000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 820    Accepted Submission(s): 202 Problem Description Given a positive integer n, your task is to find a positive integer m, which is a multiple

hdu 1664 Different Digits

Different Digits Time Limit: 10000/4000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1138    Accepted Submission(s): 296 Problem Description Given a positive integer n, your task is to find a positive integer m,

【扩展kmp+最小循环节】HDU 4333 Revolving Digits

http://acm.hdu.edu.cn/showproblem.php?pid=4333 [题意] 给定一个数字<=10^100000,每次将该数的第一位放到放到最后一位,求所有组成的不同的数比原数小的个数,相等的个数,大的个数 [思路] 这个数很大,用字符串处理 比较两个字符串的大小,一位一位很耗时,可以求出最长公共前缀,只比较最长公共前缀后一位 每次将数的最后一位放到最后一位,如abcd变成dabc,cdab,bcda,相当于abcdabcd各个后缀的前四位 这样就变成了求abcdabc