P4049 [JSOI2007]合金
题意
因为\(a+b+c=1\),因此只要知道\(a,b\),就可以知道\(c\)。也就是说只用\(a,b\)即可表示出一种材料/合金,我们将每种材料/合金表示为坐标系上的
一种材料\(a\)能表示出的合金为\(a\)。
两种材料\(a,b\)能表示出的合金为线段\(ab\)上的所有点。
三种材料\(a,b,c\)能表示出的合金为\(ab,bc,ac\)围成的三角形中所有的点。
推广可得:\(k\)种材料能表示出的合金为这\(k\)个点的凸包内的所有点。
我们枚举每一对材料\((i,j)\),满足所有需求点都在线段\(ij\)一边,之后根据在哪边连边,之后跑最小环即可。
注意特判只有重点和共线的情况。
code:
#include<bits/stdc++.h>
using namespace std;
#define re register
const int maxn=510;
const double eps=1e-8;
const int inf=0x3f3f3f3f;
int n,m,ans=inf;
int dis[maxn][maxn];
double xmin=inf,xmax=-inf,ymin=inf,ymax=-inf;
struct Point
{
double x,y;
inline double len(){return sqrt(x*x+y*y);}
Point operator+(const Point a)const{return (Point){x+a.x,y+a.y};}
Point operator-(const Point a)const{return (Point){x-a.x,y-a.y};}
Point operator*(const double k){return (Point){x*k,y*k};}
Point operator/(const double k){return (Point){x/k,y/k};}
double operator*(const Point a)const{return x*a.y-y*a.x;}
double operator&(const Point a)const{return x*a.x+y*a.y;}
}p1[maxn],p2[maxn];
inline int dcmp(double x)
{
if(fabs(x)<=eps)return 0;
return x<0?-1:1;
}
inline bool check(double x1,double x2,double x3,double x4)
{
if(dcmp(x1-x2)>0)swap(x1,x2);
return dcmp(x1-x3)<=0&&dcmp(x4-x2)<=0;
}
inline void solve(int x,int y)
{
int cnt1=0,cnt2=0;
for(re int i=1;i<=m;i++)
{
double tmp=(p1[y]-p1[x])*(p2[i]-p1[x]);
if(dcmp(tmp)<0)cnt1++;
if(dcmp(tmp)>0)cnt2++;
}
if(!cnt1&&!cnt2&&check(p1[x].x,p1[y].x,xmin,xmax)&&check(p1[x].y,p1[y].y,ymin,ymax)){puts("2");exit(0);}//共线。
if(cnt1&&cnt2)return;
if(cnt1)dis[x][y]=1;
if(cnt2)dis[y][x]=1;
}
int main()
{
scanf("%d%d",&n,&m);
double tmp;
for(re int i=1;i<=n;i++)scanf("%lf%lf%lf",&p1[i].x,&p1[i].y,&tmp);
for(re int i=1;i<=m;i++)
{
scanf("%lf%lf%lf",&p2[i].x,&p2[i].y,&tmp);
xmin=min(xmin,p2[i].x),xmax=max(xmax,p2[i].x);
ymin=min(ymin,p2[i].y),ymax=max(ymax,p2[i].y);
}
bool flag=1;
for(re int i=1;i<=n;i++)if(dcmp(p1[i].x-p1[1].x)!=0||dcmp(p1[i].y-p1[1].y)!=0)flag=0;
for(re int i=1;i<=n;i++)if(dcmp(p2[i].x-p2[1].x)!=0||dcmp(p2[i].y-p2[1].y)!=0)flag=0;
if(flag){puts("1");return 0;}//重点。
memset(dis,0x3f,sizeof(dis));
for(re int i=1;i<=n;i++)
for(re int j=i+1;j<=n;j++)
solve(i,j);
for(re int k=1;k<=n;k++)
for(re int i=1;i<=n;i++)
{
if(dis[i][k]==inf)continue;
for(re int j=1;j<=n;j++)dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
for(re int i=1;i<=n;i++)ans=min(ans,dis[i][i]);
if(ans==inf||ans<2)puts("-1");
else printf("%d",ans);
return 0;
}