1998 ACM Finals, Dan Adkins
农夫约翰从来只用调配得最好的饲料来喂他的奶牛。饲料用三种原料调配成:大麦,燕麦和小麦。他知道自己的饲料精确的配比,在市场上是买不到这样的饲料的。他只好购买其他三种混合饲料(同样都由三种麦子组成),然后将它们混合,来调配他的完美饲料。
给出三组整数,表示 大麦:燕麦:小麦 的比例,找出用这三种饲料调配 x:y:z 的饲料的方法。
例如,给出目标饲料 3:4:5 和三种饲料的比例:
1:2:3 3:7:1 2:1:2
你必须编程找出使这三种饲料用量最少的方案,要是不能用这三种饲料调配目标饲料,输出“NONE”。“用量最少”意味着三种饲料的用量(整数)的和必须最小。 对于上面的例子,你可以用8份饲料1,1份饲料2,和5份饲料3,来得到7份目标饲料:
8*(1:2:3) + 1*(3:7:1) + 5*(2:1:2) = (21:28:35) = 7*(3:4:5)
表示饲料比例的整数,都是小于100的非负整数。表示各种饲料的份数的整数,都小于100。一种混合物的比例不会由其他混合物的比例直接相加得到。
PROGRAM NAME: ratios
INPUT FORMAT:
(file ratios.in)
Line 1: 三个用空格分开的整数,表示目标饲料
Line 2..4: 每行包括三个用空格分开的整数,表示农夫约翰买进的饲料的比例
OUTPUT FORMAT:
(file ratios.out)
输出文件要包括一行,这一行要么有四个整数,要么是“NONE”。前三个整数表示三种饲料的份数,用这样的配比可以得到目标饲料。第四个整数表示混合三种饲料后得到的目标饲料的份数。
3 4 5 1 2 3 3 7 1 2 1 2
8 1 5 7
分析:
1.可暴力枚举,只需要枚举100*100*100种情况,然后选取符合条件的解。
2.方程:设三中饲料份数分别为x1,x2,x3,则:
x1+3*x2+2*x3=3k
2*x1+7*x2+x3=4k
3*x1+x2+2*x3=5k
首先求出系数矩阵D,判断是否有解,如果有解可以继续用克莱姆法则或者高斯消元法对每一个k解出方程。
代码:
1 /* 2 ID:138_3531 3 PROG:ratios 4 LANG:C++ 5 */ 6 7 8 #include <algorithm> 9 #include <iostream> 10 #include <string> 11 #include <cstring> 12 #include <cstdio> 13 #include <cmath> 14 15 16 using namespace std; 17 const int maxn = 5; 18 int equ, var; // 有equ个方程,var个变元。增广阵行数为equ, 分别为到equ - 1,列数为var + 1,分别为到var. 19 int a[maxn][maxn]; 20 int x[maxn]; // 解集. 21 bool free_x[maxn]; // 判断是否是不确定的变元. 22 int free_num; 23 void Debug() 24 { 25 int i, j; 26 for (i = 0; i < equ; i++) 27 { 28 for (j = 0; j < var + 1; j++) 29 { 30 cout << a[i][j] << " "; 31 } 32 cout << endl; 33 } 34 cout << endl; 35 } 36 inline int gcd(int a, int b) 37 { 38 int t; 39 while (b != 0) 40 { 41 t = b; 42 b = a % b; 43 a = t; 44 } 45 return a; 46 } 47 inline int lcm(int a, int b) 48 { 49 return a * b / gcd(a, b); 50 } 51 // 高斯消元法解方程组(Gauss-Jordan elimination).(-2表示有浮点数解,但无整数解,-1表示无解,表示唯一解,大于表示无穷解,并返回自由变元的个数) 52 int Gauss(void) 53 { 54 int i, j, k; 55 int max_r; // 当前这列绝对值最大的行. 56 int col; // 当前处理的列. 57 int ta, tb; 58 int LCM; 59 int temp; 60 int free_x_num; 61 int free_index; 62 // 转换为阶梯阵. 63 col = 0; // 当前处理的列. 64 for (k = 0; k < equ && col < var; k++, col++) 65 { // 枚举当前处理的行. 66 // 找到该col列元素绝对值最大的那行与第k行交换.(为了在除法时减小误差) 67 max_r = k; 68 for (i = k + 1; i < equ; i++) 69 { 70 if (abs(a[i][col]) > abs(a[max_r][col])) max_r = i; 71 } 72 if (max_r != k) 73 { // 与第k行交换. 74 for (j = k; j < var + 1; j++) swap(a[k][j], a[max_r][j]); 75 } 76 if (a[k][col] == 0) 77 { // 说明该col列第k行以下全是了,则处理当前行的下一列. 78 k--; continue; 79 } 80 for (i = k + 1; i < equ; i++) 81 { // 枚举要删去的行. 82 if (a[i][col] != 0) 83 { 84 LCM = lcm(abs(a[i][col]), abs(a[k][col])); 85 ta = LCM / abs(a[i][col]), tb = LCM / abs(a[k][col]); 86 if (a[i][col] * a[k][col] < 0) tb = -tb; // 异号的情况是两个数相加. 87 for (j = col; j < var + 1; j++) 88 { 89 a[i][j] = a[i][j] * ta - a[k][j] * tb; 90 } 91 } 92 } 93 } 94 // Debug(); 95 // 1. 无解的情况: 化简的增广阵中存在(0, 0, ..., a)这样的行(a != 0). 96 for (i = k; i < equ; i++) 97 { // 对于无穷解来说,如果要判断哪些是自由变元,那么初等行变换中的交换就会影响,则要记录交换. 98 if (a[i][col] != 0) return -1; 99 } 100 // 2. 无穷解的情况: 在var * (var + 1)的增广阵中出现(0, 0, ..., 0)这样的行,即说明没有形成严格的上三角阵. 101 // 且出现的行数即为自由变元的个数. 102 if (k < var) 103 { 104 // 首先,自由变元有var - k个,即不确定的变元至少有var - k个. 105 for (i = k - 1; i >= 0; i--) 106 { 107 // 第i行一定不会是(0, 0, ..., 0)的情况,因为这样的行是在第k行到第equ行. 108 // 同样,第i行一定不会是(0, 0, ..., a), a != 0的情况,这样的无解的. 109 free_x_num = 0; // 用于判断该行中的不确定的变元的个数,如果超过个,则无法求解,它们仍然为不确定的变元. 110 for (j = 0; j < var; j++) 111 { 112 if (a[i][j] != 0 && free_x[j]) free_x_num++, free_index = j; 113 } 114 if (free_x_num > 1) continue; // 无法求解出确定的变元. 115 // 说明就只有一个不确定的变元free_index,那么可以求解出该变元,且该变元是确定的. 116 temp = a[i][var]; 117 for (j = 0; j < var; j++) 118 { 119 if (a[i][j] != 0 && j != free_index) temp -= a[i][j] * x[j]; 120 } 121 x[free_index] = temp / a[i][free_index]; // 求出该变元. 122 free_x[free_index] = 0; // 该变元是确定的. 123 } 124 return var - k; // 自由变元有var - k个. 125 } 126 // 3. 唯一解的情况: 在var * (var + 1)的增广阵中形成严格的上三角阵. 127 // 计算出Xn-1, Xn-2 ... X0. 128 for (i = var - 1; i >= 0; i--) 129 { 130 temp = a[i][var]; 131 for (j = i + 1; j < var; j++) 132 { 133 if (a[i][j] != 0) temp -= a[i][j] * x[j]; 134 } 135 if (temp % a[i][i] != 0) return -2; // 说明有浮点数解,但无整数解. 136 x[i] = temp / a[i][i]; 137 } 138 return 0; 139 } 140 141 142 int tmp[5][5]; 143 int main() 144 { 145 freopen("ratios.in","r",stdin); 146 freopen("ratios.out","w",stdout); 147 int xx,yy,zz; 148 cin>>xx>>yy>>zz; 149 equ=var=3; 150 int i,j; 151 for(i=0;i<3;i++) 152 for(j=0;j<3;j++) 153 cin>>tmp[j][i]; 154 155 156 for(i=1;i<105;i++) 157 { 158 for(int k=0;k<3;k++) 159 for(j=0;j<3;j++) 160 a[j][k]=tmp[j][k]; 161 a[0][3]=i*xx; 162 a[1][3]=i*yy; 163 a[2][3]=i*zz; 164 165 166 free_num = Gauss(); 167 if(free_num>=0) 168 { 169 170 171 if(free_num==0) 172 { 173 if(x[0]<0||x[1]<0||x[2]<0) 174 continue; 175 printf("%d %d %d %d\n",x[0],x[1],x[2],i); 176 } 177 else 178 { 179 180 181 if((!free_x[0]&&x[0]<0)||(!free_x[1]&&x[1]<0)||(!free_x[2]&&x[2]<0)) 182 continue; 183 184 185 for(j=0;j<3;j++) 186 { 187 if(free_x[j]) 188 printf("0 "); 189 else 190 printf("%d ",x[j]); 191 } 192 printf("%d\n",i); 193 } 194 break; 195 } 196 } 197 if(i==105) 198 printf("NONE\n"); 199 return 0; 200 }