zoukankan      html  css  js  c++  java
  • [BZOJ1027][JSOI2007]合金(凸包+最短路)

    [BZOJ1027][JSOI2007]合金(凸包+最短路)

    题面

    某公司加工一种由铁、铝、锡组成的合金。他们的工作很简单。首先进口一些铁铝锡合金原材料,不同种类的原材料中铁铝锡的比重不同。然后,将每种原材料取出一定量,经过融解、混合,得到新的合金。新的合金的铁铝锡比重为用户所需要的比重。

    现在,用户给出了n种他们需要的合金,以及每种合金中铁铝锡的比重。公司希望能够订购最少种类的原材料,并且使用这些原材料可以加工出用户需要的所有种类的合金。

    分析

    容易发现(c=1-a-b),那么就没必要考虑(c),只考虑(a,b). 我们先考虑2种合金((a_i,b_i)(a_j,b_j))混合的情况,设比例分别为(lambda,mu(lambda+mu=1)),那得到的新合金就是((lambda a_i+mu a_j,lambda b_i+mu b_j)).把((a,b))看成平面直角坐标系上的一个点(A_i),设原点为(O).那么得到的新合金为(lambda vec{OA_i}+mu vec{OA_j}). 由于(lambda+mu=1)根据向量知识可以得到,新的合金在点(A_i,A_j)的连线上。
    xl.JPG
    再考虑3种的情况.如上图,在两两相加形成的线段上任取两个点,新的合金在两点连线上。无数多的线段叠加在一起,就得到了一个三角形区域。以此类推,(n)种合金能够合成的金属对应的点在包含这(n)个点的多边形内

    那么问题变成了,选择最少的点,使得这些点构成的凸包内部含有所有需要的点。我们可以枚举可能成为凸包的线段(A_iA_j),从(i)(j)连一条边权为1的边。判断是否可能在凸包上,只需要用叉积判断是否存在点在向量(vec{A_iA_j})右侧即可,注意特判共线但不在线段上的情况。这一步的复杂度是(O(n^2m)).这样图上一个回路就对应一个凸包,Floyd求最小环即可。

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring> 
    #include<cmath>
    #define maxn 500
    #define eps 1e-7
    #define INF 0x3f3f3f3f
    using namespace std;
    typedef double db;
    struct Vector{
    	db x;
    	db y;
    	Vector(){
    		
    	}
    	Vector(db _x,db _y){
    		x=_x;
    		y=_y;
    	}
    	friend Vector operator + (Vector p,Vector q){
    		return Vector(p.x+q.x,p.y+q.y);
    	}
    	friend Vector operator - (Vector p,Vector q){
    		return Vector(p.x-q.x,p.y-q.y); 
    	}
    };
    db cross(Vector p,Vector q){
    	return p.x*q.y-p.y*q.x;
    }
    db dot(Vector p,Vector q){
    	return p.x*q.x+p.y*q.y;
    }
    inline int sgn(double x){
    	if(fabs(x)<=eps) return 0;
    	else if(x>0) return 1;
    	else return -1; 
    }
    
    int n,m;
    Vector a[maxn+5],b[maxn+5];
    int dist[maxn+5][maxn+5]; 
    bool check(int p,int q){
    	for(int i=1;i<=m;i++){
    		int f=sgn(cross(a[q]-a[p],b[i]-a[p]));
    		if(f<0) return 0;//如果有点在PQ右侧,说明一定不是凸包的边
    		if(f==0&&sgn(dot(b[i]-a[p],b[i]-a[q]))>0) return 0;//如果共线但不在线段PQ上 
    	}
    	return 1;
    }
    int floyd(){//找出包含所有b[i]的边数最少的凸包,因为有向图,直接输出dist[i][i] 
    	int ans=INF;
    	for(int k=1;k<=n;k++){ 
    		for(int i=1;i<=n;i++){
    			for(int j=1;j<=n;j++) dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);
    		}
    	} 
    	for(int i=1;i<=n;i++) ans=min(ans,dist[i][i]);
    	return ans;
    }
    int main(){
    	db u,v,w;
    	scanf("%d %d",&n,&m);
    	for(int i=1;i<=n;i++){
    		scanf("%lf %lf %lf",&u,&v,&w);
    		a[i]=Vector(u,v);
    	}	
    	for(int i=1;i<=m;i++){
    		scanf("%lf %lf %lf",&u,&v,&w);
    		b[i]=Vector(u,v);
    	}
    	memset(dist,0x3f,sizeof(dist));
    	for(int i=1;i<=n;i++){
    //		dist[i][i]=0;
    		for(int j=1;j<=n;j++){
    			//看看i->j这条边是否可能成为凸包的边 
    			if(check(i,j)){
    //				printf("%d->%d
    ",i,j);
    				dist[i][j]=1;
    			}
    		}
    	}
    //	memcpy(dist,edge,sizeof(edge));
    	int ans=floyd();
    	if(ans>n) ans=-1;
    	printf("%d
    ",ans);	
    }
    
  • 相关阅读:
    Linux连接字符串
    【aws】
    运行mlflow命令报错 The 'nose' distribution was not found and is required by nose-exclude
    【apt install】Unable to locate package python3-pip
    python package打包发布,安装
    【mlflow】mlflow打包、启动、换用mysql backend、mysql配置
    【mlflow】打包:npm run build + python setup.py sdist
    【css flex】将多个<div>放在同一行
    【react 条件渲染】在render的html中使用 三元运算符 进行条件渲染
    由于无法验证发布者 所以windows阻止此软件
  • 原文地址:https://www.cnblogs.com/birchtree/p/13573499.html
Copyright © 2011-2022 走看看