[bzoj1027][JSOI2007]合金
来自FallDream的博客,未经允许,请勿转载,谢谢。
某公司加工一种由铁、铝、锡组成的合金。他们的工作很简单。首先进口一些铁铝锡合金原材料,不同种类的
原材料中铁铝锡的比重不同。然后,将每种原材料取出一定量,经过融解、混合,得到新的合金。新的合金的铁铝
锡比重为用户所需要的比重。 现在,用户给出了n种他们需要的合金,以及每种合金中铁铝锡的比重。公司希望能
够订购最少种类的原材料,并且使用这些原材料可以加工出用户需要的所有种类的合金。
n,m<=500
看到这道神题根本没头绪,只好看看题解。
因为三个参数加起来等于1,所以我们可以忽略第三个,用前两维表示一个平面上的点,两个材料能混合出来的材料是这两个点之间的线段,所以题目转换为求尽量少的点,使得这些点的凸包可以包住需要的材料的凸包。具体实现,如果所有需要的材料的点都在一条线段左边,那么这两个点之间连边,然后floyd求最小环。复杂度n^3
注意各种特判。如果全部都重点输出1,如果一条直线覆盖了所有的点判断一下是否都在线段上,是就输出2。最后要判断答案必须大于2.
#include<iostream> #include<cstring> #include<cstdio> #define INF 1000000000 #define MN 500 #define eps 1e-8 using namespace std; inline int read() { int x = 0 , f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x * f; } int f[MN+5][MN+5]; int n,m;double Xmn=INF,Xmx=-INF,Ymn=INF,Ymx=-INF; struct P{double x,y; P(double x=0,double y=0):x(x),y(y){} double operator^(P b){return x*b.y-y*b.x;} P operator-(P b){return P(x-b.x,y-b.y);} }p[MN+5],q[MN+5]; bool check(double x1,double x2,double x3,double x4) { if(x1>x2)swap(x1,x2); return x1<=x3&&x4<=x2; } int solve(int x,int y) { int num1=0,num2=0; for(int i=1;i<=m;i++) { double crs=(p[y]-p[x])^(q[i]-p[x]); if(crs<-eps)num2++; if(crs>eps) num1++; } if(!num1&&!num2&&check(p[x].x,p[y].x,Xmn,Xmx)&&check(p[x].y,p[y].y,Ymn,Ymx))return -1; if(num1*num2)return 0; if(num1)return 2; if(num2)return 1; return 3; } bool special() { for(int i=2;i<=n;i++) if(p[i].x!=p[1].x||p[i].y!=p[1].y)return 0; for(int i=1;i<=m;i++) if(q[i].x!=p[1].x||q[i].y!=p[1].y)return 0; return 1; } int main() { n=read();m=read();double d; for(int i=1;i<=n;i++) scanf("%lf%lf%lf",&p[i].x,&p[i].y,&d); for(int i=1;i<=m;i++) { scanf("%lf%lf%lf",&q[i].x,&q[i].y,&d); Xmn=min(Xmn,q[i].x);Xmx=max(Xmx,q[i].x); Ymn=min(Ymn,q[i].y);Ymx=max(Ymx,q[i].y); } if(special())return 0*puts("1"); memset(f,63,sizeof(f)); for(int i=1;i<n;i++) for(int j=i+1;j<=n;j++) { int res=solve(i,j); if(res==-1)return 0*puts("2"); if(res&1)f[i][j]=1; if(res&2)f[j][i]=1; } for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) if(f[i][k]<=INF) for(int j=1;j<=n;j++) f[i][j]=min(f[i][j],f[i][k]+f[k][j]); int minn=INF; for(int i=1;i<=n;i++) minn=min(minn,f[i][i]); if(minn>2&&minn<INF)printf("%d\n",minn);else puts("-1"); return 0; }
FallDream代表秋之国向您问好!
欢迎您来我的博客www.cnblogs.com/FallDream