1027: [JSOI2007]合金
Time Limit: 4 Sec Memory Limit: 162 MB
Submit: 2671 Solved: 703
[Submit][Status][Discuss]
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
题意:见分析
分析:我把n和m的意义相调转
即n代表原材料种数,m代表所需产品数
显然,第三维不需要,因为a+b+c==1,前两维确定即确定第三维
当n==2时,若一个产品可以通过这两种原材料组合而成那么一定有
materia1.a*k+materia2.a*(1-k) == product.a
materia1.b*k+materia2.b*(1-k) == product.b
等等,这个形式。。。如果把它们的a,b表示成二元组(a, b),那么。。。。
不就是说明如果把a当成横坐标,b当成纵坐标,
那么这两种原材料所能组成的产品一定在两端点为(materia1.a, materia1.b)(materia2.a, materia2.b)的线段上吗?
那么如果n == 3时,显然根据向量的加减,所能组成的产品一定在这三点围成的三角形里
当n==k时,同理,所能组成的产品一定在这几个点所围成的图形里(即这几个原材料的点所形成的凸包里)
但显然,构成凸包的最少点未必就是包住产品点的最少点数
那么我们探究一下,
我们逆时针看那个围住产品点的图形
并称一个由原材料点构成的 能围住产品点的 图形为合法图形
探究合法图形的边有什么性质
显然,如果一条边可能属于一个合法图形,那么所有的产品点都在它的一侧,我们假定都在这条边的逆时针方向
(因为我们使用叉积判断,所以要这样,注意,我们是逆时针看那个合法图形的)
所以首先我们要枚举任意两个原材料点,判断它们所连的边是否合法。。。
然后我们根据所有的合法边,尝试围成一个点数最少的合法图形
等等。。。。。如果我们把每条边的长度设为1,那么点数的多少不就是那个图形(环)的长度吗!?
不就是最小环吗?
对于最小环,我推荐一篇文章
http://www.cnblogs.com/Yz81128/archive/2012/08/15/2640940.html
我个人的理解:
一下见解不局限于本题,普适于所有有向图的最小环
每次在进行floyd的更新前(保证当前所有的最短路不包含k),算出环上的点最大为k(一定要有k)的最小的环
这样更新ans = min(ans, dis[i][j]+map[j][k]+map[k][i])
因为当前没有更新dis[i][j]所代表的最短路不包含k,如此便枚举出所有的 环上的点最大为k的环
然后再进行更新
当然由于本题的特殊性,并不需要完整的最小环算法
只需要求一遍点 i->点 i(就是回到自己)的最短路即可(同原来的floyd)
提供代码
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <deque> 6 #include <vector> 7 #include <queue> 8 #include <iostream> 9 #include <algorithm> 10 #include <map> 11 #include <set> 12 #include <ctime> 13 using namespace std; 14 typedef long long LL; 15 typedef double DB; 16 #define For(i, s, t) for(int i = (s); i <= (t); i++) 17 #define Ford(i, s, t) for(int i = (s); i >= (t); i--) 18 #define Rep(i, t) for(int i = (0); i < (t); i++) 19 #define Repn(i, t) for(int i = ((t)-1); i >= (0); i--) 20 #define rep(i, x, t) for(int i = (x); i < (t); i++) 21 #define MIT (2147483647) 22 #define INF (1000000001) 23 #define MLL (1000000000000000001LL) 24 #define sz(x) ((int) (x).size()) 25 #define clr(x, y) memset(x, y, sizeof(x)) 26 #define puf push_front 27 #define pub push_back 28 #define pof pop_front 29 #define pob pop_back 30 #define ft first 31 #define sd second 32 #define mk make_pair 33 inline void SetIO(string Name) { 34 string Input = Name+".in", 35 Output = Name+".out"; 36 freopen(Input.c_str(), "r", stdin), 37 freopen(Output.c_str(), "w", stdout); 38 } 39 40 const int N = 510; 41 const DB Eps = 1e-6; 42 inline int Cmp(DB a, DB b) ; 43 struct Point { 44 DB x, y; 45 46 inline bool operator ==(const Point &A) const { 47 if(!Cmp(x, A.x) && !Cmp(y, A.y)) return 1; 48 return 0; 49 } 50 } A[N], B[N]; 51 int n, m; 52 int Dp[N][N], Map[N][N]; 53 54 inline void Input() { 55 scanf("%d%d", &n, &m); 56 DB c; 57 For(i, 1, n) scanf("%lf%lf%lf", &A[i].x, &A[i].y, &c); 58 For(i, 1, m) scanf("%lf%lf%lf", &B[i].x, &B[i].y, &c); 59 } 60 61 inline int Cmp(DB a, DB b) { 62 if(a < b-Eps) return -1; 63 if(a > b+Eps) return 1; 64 return 0; 65 } 66 67 inline bool Cover(Point x) { 68 For(i, 1, m) 69 if(!(x == B[i])) return 0; 70 return 1; 71 } 72 73 inline void Exit(string Ans) { 74 cout<<Ans<<"\n"; 75 exit(0); 76 } 77 78 inline DB Multi(Point O, Point A, Point B) { 79 return (A.x-O.x)*(B.y-O.y)-(A.y-O.y)*(B.x-O.x); 80 } 81 82 inline bool Couple(Point O, Point A) { 83 For(i, 1, m) { 84 if(Cmp(Multi(O, A, B[i]), 0.0)) return 0; 85 DB Left = O.x, Right = A.x, Up = O.y, Down = A.y; 86 if(Left > Right) swap(Left, Right); 87 if(Down > Up) swap(Up, Down); 88 if(Cmp(B[i].x, Left) < 0 || Cmp(B[i].x, Right) > 0 || 89 Cmp(B[i].y, Down) < 0 || Cmp(B[i].y, Up) > 0) return 0; 90 } 91 return 1; 92 } 93 94 inline bool Judge(Point O, Point A) { 95 For(i, 1, m) 96 if(Cmp(Multi(O, A, B[i]), 0.0) < 0) return 0; 97 return 1; 98 } 99 100 inline void Solve() { 101 if(!m) Exit("0"); 102 For(i, 1, n) 103 if(Cover(A[i])) Exit("1"); 104 For(i, 1, n) 105 For(j, i+1, n) 106 if(Couple(A[i], A[j])) Exit("2"); 107 108 For(i, 1, n) 109 For(j, 1, n) Dp[i][j] = Map[i][j] = 10000; 110 For(i, 1, n) 111 For(j, 1, n) { 112 if(i == j) continue; 113 if(Judge(A[i], A[j])) Dp[i][j] = Map[i][j] = 1; 114 } 115 116 int Ans = 10000; 117 For(k, 1, n) { 118 For(i, 1, k-1) 119 For(j, 1, k-1) 120 Ans = min(Ans, Dp[i][j]+Map[j][k]+Map[k][i]); 121 122 For(i, 1, n) 123 For(j, 1, n) 124 Dp[i][j] = min(Dp[i][j], Dp[i][k]+Dp[k][j]); 125 } 126 127 if(Ans > n) Ans = -1; 128 printf("%d\n", Ans); 129 } 130 131 int main() { 132 #ifndef ONLINE_JUDGE 133 SetIO("1027"); 134 #endif 135 Input(); 136 Solve(); 137 return 0; 138 }
表示这题我原来想挫了,本来想先把原材料点搞个凸包,产品点搞个凸包,然后再用dp求原材料凸包上最少用多少