USACO / Feed Ratios (枚举||克莱姆法则||高斯消元)

 

Feed Ratios饲料调配

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”。前三个整数表示三种饲料的份数,用这样的配比可以得到目标饲料。第四个整数表示混合三种饲料后得到的目标饲料的份数。

SAMPLE INPUT

3 4 5
1 2 3
3 7 1
2 1 2 

SAMPLE OUTPUT

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解出方程。
代码:

View Code
  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 }


 
举杯独醉,饮罢飞雪,茫然又一年岁。 ------AbandonZHANG
原文地址:https://www.cnblogs.com/AbandonZHANG/p/2598251.html