bzoj 1027 合金【凸包】

解题思路:

由于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 long
using 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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值