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 }
 
   

 

 

你可能感兴趣的:(USACO)