BZOJ1027: [JSOI2007]合金

呼~终于是过了这道题了,开始伟大的计算几何!

这道题做法不是很难想到吧,输入的三个合金的比例,因为三者之和等于1,所以只需任取两个即可确定第三个(样例还有错我去!)。

我们将每种合金的两个参数看做平面上的点,那么两种合金互相混合可以得到的合金的成分必然是在以这两个点为端点的线段上。

而三种及以上种合金混合,得到的合金的成分必然是在所有合金所形成的凸包上。

那么,这道题目就可抽象成这样:所取的合金集合的凸包要完全“包住”客户所需合金的集合的凸包。

又由于n,m都小于等于500,所以我们可以采取暴力的O(n3)的算法:

直接枚举从合金i到合金j的向量ij,若所有点均在ij的左边,那么在一张有向图G中由i向j连一条有向边,最后floyd求这张有向图的最小环即可。

P.S.这题网上的std有80%是有问题的,各种特殊情况没判断,数据弱啊!

调试小结

  1.要特判所有合金都在一点的情况。

  2.要特判有客户所需合金出现在两种提供合金所在直线上的情况(这个相当麻烦)。

  3.要注意double比较时的各种eps。

附:几何画板确实好用啊,调计算几何相当爽,可惜考试不让用,还是要想别的法子。

 1 /**************************************************************

 2     Problem: 1027

 3     User: zhuohan123

 4     Language: C++

 5     Result: Accepted

 6     Time:1992 ms

 7     Memory:2304 kb

 8 ****************************************************************/

 9  

10 #include <iostream>

11 #include <cstdio>

12 #include <cmath>

13 using namespace std;

14 const double eps=1e-10;

15 struct Point{

16     double x,y;

17     Point(){x=y=0;}

18     Point(double X,double Y){x=X,y=Y;}

19 }peo[510],met[510];

20 Point V(Point start,Point end){return Point(end.x-start.x,end.y-start.y);}

21 double crossP(Point a,Point b){return a.x*b.y-b.x*a.y;}

22 bool check_line(Point ls,Point le,Point p)

23 {

24     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));

25 }

26 int g[510][510];

27 int main(int argc, char *argv[])

28 {

29     //freopen("1.in","r",stdin);

30     //freopen("1.out","w",stdout);

31     int m,n;scanf("%d%d",&m,&n);

32     double temp;

33     for(int i=1;i<=m;i++)scanf("%lf%lf%lf",&met[i].x,&met[i].y,&temp);

34     for(int i=1;i<=n;i++)scanf("%lf%lf%lf",&peo[i].x,&peo[i].y,&temp);

35     for(int i=1;i<=m;i++)

36     {

37         bool flag=true;

38         for(int j=1;j<=n;j++)

39             if(abs(met[i].x-peo[j].x)>eps||abs(met[i].y-peo[j].y)>eps)

40                 flag=false;

41         if(flag){printf("1\n");return 0;}

42     }

43     for(int i=1;i<=m;i++)

44         for(int j=1;j<=m;j++)

45         g[i][j]=1000000;

46     for(int i=1;i<=m;i++)

47         for(int j=1;j<=m;j++)

48         if(i!=j)

49         {

50             if(abs(met[i].x-met[j].x)<eps&&abs(met[i].y-met[j].y)<eps)continue ;  

51             int can=true;

52             for(int k=1;k<=n;k++)

53                 if(crossP(V(met[i],met[j]),V(met[i],peo[k]))<-eps)can=false;

54             if(can)

55             {

56                 for(int k=1;k<=n;k++)

57                 {

58                     double cp=crossP(V(met[i],met[j]),V(met[i],peo[k]));

59                     if(cp<eps&&cp>-eps&&(!check_line(met[i],met[j],peo[k])))can=false;

60                 }

61             }

62             if(can)g[i][j]=1;

63         }

64     /*cerr<<"======="<<endl;

65     for(int i=1;i<=m;i++)

66         for(int j=1;j<=m;j++)

67         if(g[i][j]==1)

68         cerr<<char(i+'A'-1)<<"->"<<char(j+'A'-1)<<endl;*/

69     int mincir=2147483647;

70     for(int k=1;k<=m;k++)

71         for(int i=1;i<=m;i++)

72             for(int j=1;j<=m;j++)

73             if(g[i][j]>g[i][k]+g[k][j])

74             g[i][j]=g[i][k]+g[k][j];

75     for(int i=1;i<=m;i++)

76         for(int j=1;j<=m;j++)

77         {

78             if(i!=j&&g[i][j]+g[j][i]<mincir)mincir=g[i][j]+g[j][i];

79             if(i==j&&g[i][j]<mincir)mincir=g[i][j];

80         }

81     if(mincir>m)printf("-1\n");

82     else printf("%d\n",mincir);

83     return 0;

84 }

 

你可能感兴趣的:(2007)