bzoj 1027 合金【凸包】

来源:互联网 发布:js date 字符串 编辑:程序博客网 时间:2024/05/16 19:39

解题思路:

由于a+b+c=1,所以确定了a,b,那么c就被确定了,所以c这一维可以无视。

把一种合金(a,b)看做平面上的一个点。

那对于两个点A(a1,b1),B(a2,b2),那么线段AB就是它们可组成的合金,因为线段AB上的每个点都可表示为(xa1+(1-x)a2,xb1+(1-x)b2)(可以用向量证明)。

如果再加入一个点C,那线段AB上任意一点到C的线段上所表示的合金都可炼成,即三角形ABC中任意一点。
加以推广可得几个点所成凸包即为它们能炼成的合金。

于是我们就把问题转化成了这样:给定两个点集A和B,求A中最小的一个子集S,使B中所有的点在S的凸包内部
这个问题怎么处理呢?这里用到一个十分巧妙的方法。

枚举A点集两点i,j(i可以等于j)若B点集中的所有点都在向量i->j的左侧或线段ij上,就连接一条i->j的单向边。
即 若任意B点集中的点k满足(k->i)×(k->j)>0||(k->i)×(k->j)==0&&(k->i)·(k->j)<=0 则连接一条i->j的单向边。
然后Floyd求最小环即可。

#include<iostream>#include<cstdio>#include<cstring>#include<string>#include<cmath>#include<algorithm>#include<ctime>#include<vector>#include<queue>#define ll long longusing namespace std;int getint(){    int i=0,f=1;char c;    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());    if(c=='-')c=getchar(),f=-1;    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';    return i*f;}const int N=505,INF=0x3f3f3f3f;const double eps=1e-8;int n,m,ans;int f[N][N];struct point{    double x,y;    point(){}    point(double _x,double _y):        x(_x),y(_y){}    friend inline point operator -(const point &a,const point &b)    {return point(a.x-b.x,a.y-b.y);}    friend inline double operator *(const point &a,const point &b)    {return a.x*b.y-a.y*b.x;}    friend inline double operator ^(const point &a,const point &b)    {return a.x*b.x+a.y*b.y;}}a[N],b[N];void floyd(){    for(int k=1;k<=m;k++)        for(int i=1;i<=m;i++)            for(int j=1;j<=m;j++)                f[i][j]=min(f[i][j],f[i][k]+f[k][j]);    for(int i=1;i<=m;i++)        ans=min(ans,f[i][i]);}int main(){    //freopen("lx.in","r",stdin);    //freopen("lx.out","w",stdout);    memset(f,INF,sizeof(f));    int c;    m=getint(),n=getint();    for(int i=1;i<=m;i++)        scanf("%lf%lf%lf",&a[i].x,&a[i].y,&c);    for(int i=1;i<=n;i++)        scanf("%lf%lf%lf",&b[i].x,&b[i].y,&c);    for(int i=1;i<=m;i++)        for(int j=1;j<=m;j++)        {            int k;            for(k=1;k<=n;k++)            {                double delta=(a[i]-b[k])*(a[j]-b[k]);                if(delta<-eps)break;                if(abs(delta)<eps&&((a[i]-b[k])^(a[j]-b[k]))>eps)                    break;            }            if(k==n+1)f[i][j]=1;        }    ans=INF;    floyd();    ans==INF?puts("-1"):printf("%d",ans);    return 0;}
原创粉丝点击