bzoj1027: [JSOI2007]合金
凸包,floyd求最小环。
首先第三个变量是可以由变量1,2得到的,所以可以省去。
然后如果产品在由原材料构成的凸包里,它就是可以被合成的。
所以问题就是要求包含所有产品的最小的凸包。
所以所有取到的边都在确定的一侧,所以先判断出哪些边可以取,跑floyd最小环就可以了。
#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define eps 1e-10 using namespace std; const int maxn = 500+10; const int inf = 0x3f3f3f3f; struct Point { double x,y; Point(double _x=0,double _y=0):x(_x),y(_y) {} }mat[maxn],req[maxn]; struct Vector { double x,y; double operator* (Vector b) {return x*b.y-y*b.x;} Vector(double _x=0,double _y=0):x(_x),y(_y) {} Vector(Point a,Point b):x(b.x-a.x),y(b.y-a.y) {} }; double t; int sgn(double x) { if(abs(x) < eps) return 0; else if(x>0) return 1; else return -1; } bool check_line(Point ls,Point le,Point p) { return (ls.x>p.x && le.x>p.x) || (ls.x<p.x && le.x<p.x) || (ls.y>p.y && le.y>p.y) || (ls.y<p.y && le.y<p.y) ; } int g[maxn][maxn]; int n,m; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%lf%lf%lf",&mat[i].x,&mat[i].y,&t); for(int i=1;i<=m;i++) scanf("%lf%lf%lf",&req[i].x,&req[i].y,&t); for(int i=1;i<=n;i++) { bool flag=true; for(int j=1;j<=m;j++) if(sgn(mat[i].x-req[j].x)||sgn(mat[i].y-req[j].y)) {flag=false; break;} if(flag) {printf("1\n",i); return 0;} } memset(g,0x3f,sizeof(g)); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(i!=j){ if(!sgn(mat[i].x-mat[j].x) && !sgn(mat[i].y-mat[i].y)) continue; bool able=true; for(int k=1;k<=m;k++) if(sgn(Vector(mat[i],mat[j])*Vector(mat[i],req[k]))==-1) { able=false; break; } if(able) { for(int k=1;k<=m;k++) if(sgn(Vector(mat[i],mat[j])*Vector(mat[i],req[k]))==0&&check_line(mat[i],mat[j],req[k])) { able=false; break; } } if(able) g[i][j]=1; } int res=inf; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) for(int k=1;k<=n;k++) g[i][j]=min(g[i][j],g[i][k]+g[k][j]); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { if(i!=j) res=min(res,g[i][j]+g[j][i]); else res=min(res,g[i][i]); } printf("%d\n",res>n?-1:res); return 0; }