[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);	
}
原文地址:https://www.cnblogs.com/birchtree/p/13573499.html