• 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
  • 相关阅读:
    JSP--------------------图书管理器
    Vue-router的基本使用(创建--动态跳转)
    JSP邮箱小案例(完成全部功能)
    Vue CLI3脚手架的使用
    Vue小案例测试-------------------实现购物车小模块
    JSP第六次作业提交~~~~~~~~~~~~~~~~~~~~~
    JSP项目第二阶段(使用数据库存储与注册)
    jsp第三次作业-------------------------------
    2021/4/19作业
    4.12作业
  • 原文地址:https://www.cnblogs.com/AbandonZHANG/p/2598251.html
Copyright © 2020-2023  润新知