[BZOJ1027][JSOI2007]合金

[JSOI2007]合金

Description
某公司加工一种由铁、铝、锡组成的合金。他们的工作很简单。首先进口一些铁铝锡合金原材料,不同种类的原材料中铁铝锡的比重不同。然后,将每种原材料取出一定量,经过融解、混合,得到新的合金。新的合金的铁铝锡比重为用户所需要的比重。 现在,用户给出了n种他们需要的合金,以及每种合金中铁铝锡的比重。公司希望能够订购最少种类的原材料,并且使用这些原材料可以加工出用户需要的所有种类的合金。
Input
第一行两个整数m和n(m, n ≤ 500),分别表示原材料种数和用户需要的合金种数。第2到m + 1行,每行三个实数a, b, c(a, b, c ≥ 0 且 a + b + c = 1),分别表示铁铝锡在一种原材料中所占的比重。第m + 2到m + n + 1行,每行三个实数a, b, c(a, b, c ≥ 0 且 a + b + c = 1),分别表示铁铝锡在一种用户需要的合金中所占的比重。
Output
一个整数,表示最少需要的原材料种数。若无解,则输出–1。
Sample Input
10 10
0.1 0.2 0.7
0.2 0.3 0.5
0.3 0.4 0.3
0.4 0.5 0.1
0.5 0.1 0.4
0.6 0.2 0.2
0.7 0.3 0
0.8 0.1 0.1
0.9 0.1 0
1 0 0
0.1 0.2 0.7
0.2 0.3 0.5
0.3 0.4 0.3
0.4 0.5 0.1
0.5 0.1 0.4
0.6 0.2 0.2
0.7 0.3 0
0.8 0.1 0.1
0.9 0.1 0
1 0 0
Sample Output
5

Solution
首先前两维符合要求之后第三维自然符合,无视
两个材料能合成的所有合金在它们的连线的线段上
若干材料能合成的所有合金在它们的凸包内
那么我们要用最少的点构成的凸包囊括所有的要求点

枚举两个材料,如果所有合金在它们分割成的一个半平面里,就连一条有向边

然后就是最小环

Code

#include <bits/stdc++.h>
using namespace std;

#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define per(i, r, l) for (int i = (r); i >= (l); i--)
#define MS(_, __) memset(_, __, sizeof(__))
#define MP make_pair
#define PB push_back
typedef long long ll;
typedef pair<int, int> PII;
template<typename T> inline void read(T &x){
    x = 0; T f = 1; char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    while (isdigit(ch))  {x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}

const int INF = 1e9;
const double eps = 1e-8;
struct Vec{
    double x, y;
    Vec() {}
    Vec(double _x, double _y) : x(_x), y(_y) {}
    inline double angle() const{ return atan2(y, x); }
    inline double len() const{ return sqrt(x*x+y*y); }
    inline Vec l_rot() const{ return Vec(-y, x); }
    inline Vec r_rot() const{ return Vec(y, -x); }
};
inline Vec operator + (const Vec &a, const Vec &b) { return Vec(a.x+b.x, a.y+b.y); }
inline Vec operator - (const Vec &a, const Vec &b) { return Vec(a.x-b.x, a.y-b.y); }
template<typename T> inline Vec operator * (const Vec &a, T b) { return Vec(a.x*b, a.y*b); }
template<typename T> inline Vec operator * (T a, const Vec &b) { return Vec(a*b.x, a*b.y); }
template<typename T> inline Vec operator / (const Vec &a, T b) { return Vec(a.x/b, a.y/b); }
inline double cross(const Vec &a, const Vec &b) { return a.x*b.y-a.y*b.x; }
inline double dot(const Vec &a, const Vec &b) { return a.x*b.x+a.y*b.y; }
typedef Vec Poi;
inline bool le(double x, double y) { return x-y<=eps; }
inline bool lt0(double x) { return x<-eps; }
inline bool gt0(double x) { return x>eps;}
inline bool eq0(double x) { return abs(x)<=eps; } 
inline bool between(Poi m, Poi l, Poi r) { return le(min(l.x,r.x),m.x)&&le(m.x,max(l.x,r.x)); }

int n, m;
Vec goal[510], given[510];
int g[510][510];
inline void spj(){
    if (n == 1){ bool flag = true;
        rep(i, 1, m) if (goal[i].x != given[1].x || goal[i].y != given[1].y) flag = false;
        puts(flag ? "1" : "-1");
        exit(0);
    }
}
int main(){
    read(n); read(m);

    rep(i, 1, n) scanf("%lf%lf%*lf", &given[i].x, &given[i].y);
    rep(i, 1, m) scanf("%lf%lf%*lf", &goal[i].x, &goal[i].y);
    spj();
    rep(i, 1, n) rep(j, 1, n) g[i][j] = INF;
    rep(dot1, 1, n) rep(dot2, dot1+1, n){ 
        Vec p = given[dot1]-given[dot2]; bool flag1 = false, flag2 = false, flag3 = false;
        rep(dot3, 1, m){
            double t = cross(p, goal[dot3]-given[dot2]);
            if (lt0(t)) flag1 = true;
            if (gt0(t)) flag2 = true;
            if (eq0(t) && gt0(dot(goal[dot3]-given[dot2], goal[dot3]-given[dot1]))) flag3 = true;
            if ((flag1 && flag2) || flag3) break;
        }
        if (flag1 && flag2) continue; if (flag3) continue;
        if (flag1) g[dot1][dot2] = 1; 
        else if (flag2) g[dot2][dot1] = 1;
        else g[dot1][dot2] = g[dot2][dot1] = 1;
    }

    int ans = INF;
    rep(k, 1, n) rep(i, 1, n) rep(j, 1, n) g[i][j] = min(g[i][j], g[i][k]+g[k][j]);
    rep(i, 1, n) ans = min(ans, g[i][i]);
    if (ans > n) puts("-1"); else printf("%d\n", ans);

    return 0;
}

你可能感兴趣的:(计算几何,floyed,最小环)