BZOJ1027: [JSOI2007]合金

Description

  某公司加工一种由铁、铝、锡组成的合金。他们的工作很简单。首先进口一些铁铝锡合金原材料,不同种类的
原材料中铁铝锡的比重不同。然后,将每种原材料取出一定量,经过融解、混合,得到新的合金。新的合金的铁铝
锡比重为用户所需要的比重。 现在,用户给出了n种他们需要的合金,以及每种合金中铁铝锡的比重。公司希望能
够订购最少种类的原材料,并且使用这些原材料可以加工出用户需要的所有种类的合金。

Input

  第一行两个整数m和n(m, n ≤ 500),分别表示原材料种数和用户需要的合金种数。第2到m + 1行,每行三
个实数a, b, c(a, b, c ≥ 0 且 a + b + c = 1),分别表示铁铝锡在一种原材料中所占的比重。第m + 2到m +
 n + 1行,每行三个实数a, b, c(a, b, c ≥ 0 且 a + b + c = 1),分别表示铁铝锡在一种用户需要的合金中
所占的比重。

Output

  一个整数,表示最少需要的原材料种数。若无解,则输出–1。

Sample Input

10 10
0.1 0.2 0.7
0.2 0.3 0.5
0.3 0.4 0.3
0.4 0.5 0.1
0.5 0.1 0.4
0.6 0.2 0.2
0.7 0.3 0
0.8 0.1 0.1
0.9 0.1 0
1 0 0
0.1 0.2 0.7
0.2 0.3 0.5
0.3 0.4 0.3
0.4 0.5 0.1
0.5 0.1 0.4
0.6 0.2 0.2
0.7 0.3 0
0.8 0.1 0.1
0.9 0.1 0
1 0 0

Sample Output

5

HINT

Source

计算几何+floyd求最小环
首先由于保证a+b+c=1第三维可以无视
剩下的抽象成二维坐标
然后考虑两个点能合成出来的点是在它们的连线段上
问题转化为求一个点数最少的多边形包围n个点
枚举向量a[j]-a[i],判断其他点是否在它的同一边(自己确定左右,都无所谓)
然后求最小环即可
特判掉n个点共点,以及某个点n在某个向量所在的直线上但不在线段上
#include <bits/stdc++.h>

using namespace std;

const int MAXN = 501;
const double eps = 1e-9;
const int INF = 1e9 + 7;

struct Point
{
	double x, y;
	Point operator - ( const Point &b ) const { Point c; c.x = x - b.x; c.y = y - b.y; return c; }
	double operator * ( const Point &b ) const { return x * b.y - y * b.x; }
}a[MAXN], b[MAXN];

int ans, dis[MAXN][MAXN], m, n;

double yjq;

inline bool single_point()
{
	for( int i = 1 ; i <= m ; i++ )
	{
		bool flag = true;
		for( int j = 1 ; j <= n ; j++ )
			if( abs( a[ i ].x - b[ j ].x ) > eps || abs( a[ i ].y - b[ j ].y ) > eps ) { flag = false; break; }
		if( flag ) return printf( "1\n" ), true;
	}
	return false;
}

inline bool chk(Point x, Point y, Point z)
{
	return ( x.x < z.x && y.x < z.x ) || ( x.y < z.y && y.y < z.y ) ||
		   ( x.x > z.x && y.x > z.x ) || ( x.y > z.y && y.y > z.y );
}

int main()
{
	scanf( "%d%d", &m, &n );
	for( int i = 1 ; i <= m ; i++ ) scanf( "%lf%lf%lf", &a[ i ].x, &a[ i ].y, &yjq );
	for( int i = 1 ; i <= n ; i++ ) scanf( "%lf%lf%lf", &b[ i ].x, &b[ i ].y, &yjq );
	if( single_point() ) return 0;
	for( int i = 1 ; i <= m ; i++ )
		for( int j = 1 ; j <= m ; j++ )
		{
			dis[ i ][ j ] = INF;
			if( abs( a[ i ].x - a[ j ].x ) < eps && abs( a[ i ].y - a[ j ].y ) ) continue;
			bool flag = true;
			for( int k = 1 ; k <= n ; k++ )
				if( ( a[ i ] - a[ j ] ) * ( a[ i ] - b[ k ] ) < -eps ) { flag = false; break; }
			if( flag )
			{
				for( int k = 1 ; k <= n ; k++ )
				{
					double tmp = ( a[ i ] - a[ j ] ) * ( a[ i ] - b[ k ] );
					if( fabs( tmp ) < eps && chk( a[ i ], a[ j ], b[ k ] ) ) { flag = false; break; }
				}
			}
			if( flag ) dis[ i ][ j ] = 1;
		}
	ans = INF;
	for( int k = 1 ; k <= m ; k++ )
		for( int i = 1 ; i <= m ; i++ )
			for( int j = 1 ; j <= m ; j++ )
				dis[ i ][ j ] = min( dis[ i ][ j ], dis[ i ][ k ] + dis[ k ][ j ] );
	for( int i = 1 ; i <= m ; i++ ) ans = min( ans, dis[ i ][ i ] );
	if( ans > m ) ans = -1;
	return printf( "%d\n", ans ), 0;	
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值