1. 程式人生 > >bzoj 1027: [JSOI2007]合金【凸包+Floyd】

bzoj 1027: [JSOI2007]合金【凸包+Floyd】

ref ret truct 刪掉 www. main for const 哪些

參考:https://www.cnblogs.com/zhuohan123/p/3237246.html
因為一c可以由1-a-b得出,所以刪掉c,把a,b抽象成二維平面上的點。首先考慮一個客戶需求能被哪些原料配出來:兩個原料點連線上的點都可以,要是多個原料點,那麽這些線的向量構成的凸包中的點都可以
所以得到了一個n三方算法:枚舉每兩個原料點,看是否所有需求點都在這條向量的半平面裏,是則連1,然後Floyd求最小環即可
但是有非常多惡心的特判……
1.需求點重合為一點
2.需求點出現在兩種原料點所在直線上的情況

#include<iostream>
#include<cstdio>
#include<cmath> using namespace std; const int N=505,inf=1e9; const double eps=1e-10; int m,n,a[N][N]; double c; struct dian { double x,y; dian(double X=0,double Y=0) { x=X,y=Y; } dian operator - (const dian &a) const { return dian(x-a.x,y-a.y); } }q[510
],p[510]; dian V(dian a,dian b) { return dian(b.x-a.x,b.y-a.y); } double cj(dian a,dian b) { return a.x*b.y-b.x*a.y; } bool ok(dian s,dian t,dian p) { return !((s.x>p.x&&t.x>p.x)||(s.x<p.x&&t.x<p.x)||(s.y>p.y&&t.y>p.y)||(s.y<p.y&&t.y<p.y)); } int
main() { scanf("%d%d",&m,&n); for(int i=1;i<=m;i++) scanf("%lf%lf%lf",&p[i].x,&p[i].y,&c); for(int i=1;i<=n;i++) scanf("%lf%lf%lf",&q[i].x,&q[i].y,&c); for(int i=1;i<=m;i++) { bool flag=1; for(int j=1;j<=n;j++) if(abs(p[i].x-q[j].x)>eps||abs(p[i].y-q[j].y)>eps) flag=0; if(flag) { printf("1\n"); return 0; } } for(int i=1;i<=m;i++) for(int j=1;j<=m;j++) a[i][j]=inf; for(int i=1;i<=m;i++) for(int j=1;j<=m;j++) if(i!=j) { if(abs(p[i].x-p[j].x)<eps&&abs(p[i].y-p[j].y)<eps) continue ; int can=1; for(int k=1;k<=n;k++) if(cj(p[j]-p[i],q[k]-p[i])<-eps) can=0; if(can) { for(int k=1;k<=n;k++) { double cp=cj(p[j]-p[i],q[k]-p[i]); if(cp<eps&&cp>-eps&&(!ok(p[i],p[j],q[k]))) can=0; } } if(can) a[i][j]=1; } int ans=inf; for(int k=1;k<=m;k++) for(int i=1;i<=m;i++) for(int j=1;j<=m;j++) if(a[i][j]>a[i][k]+a[k][j]) a[i][j]=a[i][k]+a[k][j]; for(int i=1;i<=m;i++) for(int j=1;j<=m;j++) { if(i!=j&&a[i][j]+a[j][i]<ans) ans=a[i][j]+a[j][i]; if(i==j&&a[i][j]<ans) ans=a[i][j]; } printf("%d\n",ans==inf?-1:ans); return 0; }

bzoj 1027: [JSOI2007]合金【凸包+Floyd】