HDU 1665 - Different Digits(幾何 + 歐拉定理)

題意:在一個平面上,給定一個由 n 個點(4 <= n <= 300)組成的一封閉筆畫圖形(第一個端點與第 n 個端點重合),求這個圖形將平面分紅幾個部分。ios

須要用到歐拉定理;數組

歐拉定理:設圖的頂點數爲 v ,邊數(三維中爲棱的個數)爲 e ,面數爲 f ,則v + f - e = 2.函數

則若求面數,只要求出頂點數 v 和邊數 e,即能得出答案 f = e + 2 - v.spa

(注意:這裏的邊數 e,是指的單獨一條線段,若中間被點分隔開,則算做多條線段而非一條)code

具體處理以下:blog

一、先枚舉任意兩條畫出的線段,看是否產生了新的交點,產生了即存入新的點數組中;string

二、將產生的交點去重(可能出現三條及以上直線交於一點的狀況);it

三、邊數 e 初始必爲 n - 1(由於起點和終點重合),而後再統計新的交點斷開產生的新邊;io

四、根據頂點數和邊數算出答案便可.模板

套用幾何模板,代碼以下(部分註釋註明了函數功能):

#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;
}
相關文章
相關標籤/搜索