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);	
    }
    
  • 相关阅读:
    买了一个 站立式办公 桌子。
    AutoCppHeader AutoHeader 自动根据CPP 或C文件 来生成头文件。
    cplusplus 库 在线管理; 类似于 python的 pip install 、nodejs 的npm模块
    var this.value 可在任意地方 声明 类成员变量。如果可以利用在C++ 那该有多好啊
    最近遇到了 timer1sec 定时调用的函数,出现了 时间久了,就不是每秒一次了,可能会慢的情况。如何解决呢?
    【我的愿景】每个程序员都应该学习编译原理,而每个人都应该学习 编程 和 设计。
    [已解决]下载chromium源码 download_from_google_storage 无法下载文件
    突发奇想-取缔宏定义,用另外一种语言来做C++的部分外层封装
    我讨厌Apple Safari浏览器的一些地方。不想用
    unix时间戳time_t与UTC时区的关系
  • 原文地址:https://www.cnblogs.com/birchtree/p/13573499.html
Copyright © 2011-2022 走看看