把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

P4049 [JSOI2007] 合金 分析

题目概述

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

现在,用户给出了 \(n\) 种他们需要的合金,以及每种合金中铁铝锡的比重。公司希望能够订购最少种类的原材料,并且使用这些原材料可以加工出用户需要的所有种类的合金。
对于全部的测试点,满足 \(1\le m,n\le 500\)\(0 \leq a_i,b_i,c_i,d_i,e_i,f_i \leq 1\),且 \(a_i+b_i+c_i=1\)\(d_i+e_i+f_i=1\),小数点后最多有六位数字。

分析

看似是三维问题,实则可以只是二维。

那么我们就将它变成一个坐标,把它当成向量,可以发现,题目就转化成了用最少多少个向量能够描述用户需要的向量并且这些向量组合的系数和为 \(1\)

对于两个向量,若系数和小于等于 \(1\),那么组成的向量一定是在它们围成的三角形的内部或者边界上面。

对于一个凸多边形,我们可以将其进行三角形分割,这样就可以得出若干个凸多边形的顶点能组成的点也在凸多边形内。

那么我们可以对每条边考虑所有的点是否在它的顺时针/逆时针一侧。假设我们希望多边形为逆时针多边形,则我们需要对每条选中的边,都有所有点在它的逆时针一侧。我们就可以考虑每个点到这条边两个端点的向量的夹角关系

我们需要角度不超过 \(\pi\),因此使用叉乘即可。

我们还需要特判向量共线的时候,这个时候我们可以用到点乘。如果两个向量的方向相同,这时候合金代表的点是在选中的线段的外面的,我们需要排除这种情况。

判断了每条边是否符合条件之后,我们再用一下 Floyd 求最小环,这样就解决问题了。

代码

时间复杂度 \(\mathcal{O}(m^2n+n^3)\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <stdlib.h>
#include <vector>
#include <cmath>
#define int long long
#define N 505
using namespace std;
struct vec{
	double x,y;
	vec(double _x = 0,double _y = 0): x(_x),y(_y){};
	vec operator-(const vec& b) {
		return vec(x - b.x,y - b.y);
	}
	double operator*(const vec& b) {
		return x * b.y - y * b.x; 
	}
}a[N],b[N];
int n,m;
int d[N][N];
double eps = 1e-9;
signed main(){
	cin >> m >> n;
	for (int i = 1;i <= m;i ++) scanf("%lf%lf%*f",&a[i].x,&a[i].y);
	for (int i = 1;i <= n;i ++) scanf("%lf%lf%*f",&b[i].x,&b[i].y);
	for (int i = 1;i <= m;i ++)
		for (int j = 1;j <= m;j ++) d[i][j] = m + 1;
	for (int i = 1;i <= m;i ++)
		for (int j = 1;j <= m;j ++) {
			bool fl = 1;
			double xm;
			for (int k = 1;k <= n;k ++)
				if ((xm = (a[j] - a[i]) * (b[k] - a[i])) > eps ||
					(fabs(xm) <= eps && (max(a[i].x,a[j].x) < b[k].x || min(a[i].x,a[j].x) > b[k].x))) {
					fl = false;
					break;
				}
			if (fl) d[i][j] = 1;
		}
	for (int k = 1;k <= m;k ++)
		for (int i = 1;i <= m;i ++)
			for (int j = 1;j <= m;j ++)
				d[i][j] = min(d[i][j],d[i][k] + d[k][j]);
	int ans = m + 1;
	for (int i = 1;i <= m;i ++) ans = min(ans,d[i][i]);
	if (ans == m + 1) puts("-1");
	else cout << ans;
	return 0;
}

posted @ 2026-07-19 16:12  high_skyy  阅读(4)  评论(0)    收藏  举报
动态线条
动态线条end
浏览器标题切换
浏览器标题切换end
💬 加载中……