USACO section 3.2 Feed Ratios(高斯定理)
1998 ACM Finals, Dan Adkins
Farmer John feeds his cows only the finest mixture of cow food, which has three components: Barley, Oats, and Wheat. While he knows the precise mixture of these easily mixable grains, he can not buy that mixture! He buys three other mixtures of the three grains and then combines them to form the perfect mixture.
Given a set of integer ratios barley:oats:wheat, find a way to combine them IN INTEGER MULTIPLES to form a mix with some goal ratio x:y:z.
For example, given the goal 3:4:5 and the ratios of three mixtures:
1:2:3 3:7:1 2:1:2
your program should find some minimum number of integer units (the `mixture') of the first, second, and third mixture that should be mixed together to achieve the goal ratio or print `NONE'. `Minimum number' means the sum of the three non-negative mixture integers is minimized.
For this example, you can combine eight units of mixture 1, one unit of mixture 2, and five units of mixture 3 to get seven units of the goal ratio:
8*(1:2:3) + 1*(3:7:1) + 5*(2:1:2) = (21:28:35) = 7*(3:4:5)
Integers in the goal ratio and mixture ratios are all non-negative and smaller than 100 in magnitude. The number of units of each type of feed in the mixture must be less than 100. The mixture ratios are not linear combinations of each other.
PROGRAM NAME: ratios
INPUT FORMAT
Line 1: | Three space separated integers that represent the goal ratios |
Line 2..4: | Each contain three space separated integers that represent the ratios of the three mixtures purchased. |
SAMPLE INPUT (file ratios.in)
3 4 5 1 2 3 3 7 1 2 1 2
OUTPUT FORMAT
The output file should contain one line containing four integers or the word `NONE'. The first three integers should represent the number of units of each mixture to use to obtain the goal ratio. The fourth number should be the multiple of the goal ratio obtained by mixing the initial feed using the first three integers as mixing ratios.
SAMPLE OUTPUT (file ratios.out)
8 1 5 7
思路:用高斯消元算出配置1份目标原料所需的具体配比(这是个小数,我们用分数来表示),然后分母求最小公倍数LCM就是目标配比份额,在将其余的依次乘上LCM就是
所需原料配比。
失误点:注意出现不够分的情况输出NONE,即是矩阵的行中出现有正有负则不可能完成配料。
具体代码:
/* ID:nealgav1 LANG:C++ PROG:ratios */ #include<fstream> using namespace std; ifstream cin("ratios.in"); ofstream cout("ratios.out"); const int mm=110; int map[mm][mm+1]; class node{public:int a,b;node(){a=0,b=1;}}ans[mm]; int var,equ; void debug(); int gcd(int a,int b) { int z; while(b!=0) { z=b;b=a%b;a=z; } return a; } int lcm(int a,int b) { return a*b/gcd(a,b); } void swap(int &a,int&b) { int z;z=a;a=b;b=z; } int abss(int a) { if(a<0)return -a; return a; } void Guss() { int k,col; for(k=0,col=0;k<var&&col<equ;k++,col++) { int i_max=k; for(int i=k+1;i<equ;i++) if(map[i][col]>i_max)i_max=i; if(i_max!=k) { for(int i=col;i<var+1;i++) swap(map[i_max][i],map[k][i]); } if(map[k][col]==0){k--;continue;} for(int i=k+1;i<equ;i++) if(map[i][col]!=0) { int LCM=lcm(abss(map[i][col]),abss(map[k][col])); int ta=LCM/map[i][col],tb=LCM/map[k][col]; if(ta*tb<0)ta=-ta; for(int j=col;j<var+1;j++) map[i][j]=map[i][j]*ta-map[k][j]*tb; ///如果全为负数则改为正数 if(map[i][var]<0) for(int j=col;j<var+1;j++)map[i][j]=-map[i][j]; } } for(int i=k;i<equ;i++) if(map[i][col]==0) { cout<<"NONE\n"; return; } ///矩阵出现是负数的说明有的不够数,所以无答案 for(int i=0;i<equ;i++) {for(int j=i;j<var+1;j++) { if(map[i][j]<0){cout<<"NONE\n";return;} } } int LCM=1,GCD=1; for(int i=k-1;i>=0;i--) { node tmp;tmp.a=map[i][var];tmp.b=1; for(int j=i+1;j<var;j++) if(map[i][j]) { LCM=lcm(tmp.b,ans[j].b); tmp.a=(LCM/tmp.b)*tmp.a-(LCM/ans[j].b)*ans[j].a*map[i][j]; GCD=gcd(LCM,tmp.a); tmp.a=tmp.a/GCD;tmp.b=LCM/GCD; } ans[i].b=tmp.b*map[i][i]; GCD=gcd(ans[i].b,tmp.a); ans[i].b=ans[i].b/GCD; ans[i].a=tmp.a/GCD; } LCM=1; for(int i=0;i<var;i++) LCM=lcm(ans[i].b,LCM); for(int i=0;i<var;i++) cout<<(LCM/ans[i].b)*ans[i].a<<" "; cout<<LCM<<"\n"; } void debug() { for(int i=0;i<equ;i++) {for(int j=0;j<var+1;j++) cout<<map[i][j]<<" "; cout<<"\n"; } } int main() { var=equ=3; for(int i=0;i<3;i++) cin>>map[i][3]; for(int i=0;i<3;i++) for(int j=0;j<3;j++) cin>>map[j][i]; ///debug(); Guss(); cin.close();cout.close(); }
USER: Neal Gavin Gavin [nealgav1] TASK: ratios LANG: C++ Compiling... Compile: OK Executing... Test 1: TEST OK [0.000 secs, 3408 KB] Test 2: TEST OK [0.000 secs, 3408 KB] Test 3: TEST OK [0.000 secs, 3408 KB] Test 4: TEST OK [0.000 secs, 3408 KB] Test 5: TEST OK [0.000 secs, 3408 KB] Test 6: TEST OK [0.000 secs, 3408 KB] All tests OK.YOUR PROGRAM ('ratios') WORKED FIRST TIME! That's fantastic -- and a rare thing. Please accept these special automated congratulations.
Here are the test data inputs:
------- test 1 ---- 5 8 0 3 5 4 1 3 0 6 2 0 ------- test 2 ---- 3 4 5 1 2 3 3 7 1 2 1 2 ------- test 3 ---- 20 22 24 4 5 5 9 5 6 12 12 14 ------- test 4 ---- 80 85 80 98 97 96 89 87 88 78 79 80 ------- test 5 ---- 99 99 99 1 0 0 0 1 0 0 0 1 ------- test 6 ---- 93 95 97 98 0 0 0 98 0 0 0 98
Keep up the good work!
Hal Burch
As there are only 1013 = 1,030,301 ways to do this, try them all and pick the best.
#include <stdio.h> /* the goal ratio */ int goal[3]; /* the ratio of the feeds */ int ratios[3][3]; /* the best solution found so far */ int min; int mloc[4]; /* amounts of ratio 1, 2, and 3, and the amount of ratio 4 prod */ /* the amount of each type of component in the feed */ int sum[3]; int main(int argc, char **argv) { FILE *fout, *fin; int lv, lv2, lv3; /* loop variables */ int t, s; /* temporary variables */ int gsum; /* the sum of the amounts of components in the goal mixture */ if ((fin = fopen("ratios.in", "r")) == NULL) { perror ("fopen fin"); exit(1); } if ((fout = fopen("ratios.out", "w")) == NULL) { perror ("fopen fout"); exit(1); } /* read in data */ fscanf (fin, "%d %d %d", &goal[0], &goal[1], &goal[2]); for (lv = 0; lv < 3; lv++) fscanf (fin, "%d %d %d", ratios[lv]+0, ratios[lv]+1, ratios[lv]+2); gsum = goal[0] + goal[1] + goal[2]; min = 301; /* worst than possible = infinity */ /* boundary case (this ensures gsum != 0) */ if (gsum == 0) { fprintf (fout, "0 0 0 0\n"); return 0; } for (lv = 0; lv < 100; lv++) for (lv2 = 0; lv2 < 100; lv2++) { /* for each amout of the first two types of mixtures */ sum[0] = lv*ratios[0][0] + lv2*ratios[1][0]; sum[1] = lv*ratios[0][1] + lv2*ratios[1][1]; sum[2] = lv*ratios[0][2] + lv2*ratios[1][2]; if (lv + lv2 > min) break; for (lv3 = 0; lv3 < 100; lv3++) { s = lv + lv2 + lv3; if (s >= min) break; /* worse than we've found already */ /* calculate a guess at the multiples of the goal we've obtained */ /* use gsum so we don't have to worry about divide by zero */ t = (sum[0] + sum[1] + sum[2]) / gsum; if (t != 0 && sum[0] == t*goal[0] && sum[1] == t*goal[1] && sum[2] == t*goal[2]) { /* we've found a better solution! */ /* update min */ min = s; /* store the solution */ mloc[0] = lv; mloc[1] = lv2; mloc[2] = lv3; mloc[3] = t; } /* add another 'bucket' of feed #2 */ sum[0] += ratios[2][0]; sum[1] += ratios[2][1]; sum[2] += ratios[2][2]; } } if (min == 301) fprintf (fout, "NONE\n"); /* no solution found */ else fprintf (fout, "%d %d %d %d\n", mloc[0], mloc[1], mloc[2], mloc[3]); return 0; }
Vlad Novakovski's Solution
When you combine multiples of mixtures, you can look at it as a multiplication of a matrix by a vector. For example, 8*(1:2:3) + 1*(3:7:1) + 5*(2:1:2) = (21:28:35) = 7*(3:4:5) can be seen as
[ 1 3 2 ] [ 8 ] [ 2 7 1 ] * [ 1 ] = 7 [3 4 5] [ 3 1 2 ] [ 5 ]
The matrix and the goal ratio vector (3:4:5 in this case) are given; what we have to find is the multiple vector (8:1:5 in this case) and the proportionality costant (7 here). This is like solving a system of linear equations. We can write it as
AX = kB.
Now, if we use Cramer's Rule, and let D = determinant of A, then
X_1 = k D_1 / D X_2 = k D_2 / D X_3 = k D_3 / D,
where D_1 is the determinant of the matrix A with the 1st column is replaced by B, D_2 is the determinant of the matrix A with the 2nd column is replaced by B, D_3 is the determinant of the matrix A with the 3rd column is replaced by B. (see a Linear Algebra textbook on why this works.) ,P> We are looking for integral solutions. If D = 0, no solutions. Otherwise, let k = D, and then X_1 = D_1, etc. If these values (X_1,X_2,X_3, _and_ k) all have a greatest common factor above 1, divide them all by that factor, as we are looking for the smallest possible solutions.
Now, if some of the numbers is greater than 100, we have not found a feasible solution, so we output `NONE'. Otherwise, the triple (X_1,X_2,X_3) is output, as well as the value k.