poj2947(高斯消元解同模方程组)
题目链接:http://poj.org/problem?id=2947
题意:有n 种装饰物,m 个已知条件,每个已知条件的描述如下:
p start end
a1, a2......ap (1<= ai <= n)
第一行表示从星期 start 到星期 end 一共生产了p 件装饰物 (工作的天数为end - start + 1 + 7*x, 加 7*x 是因为它可能生产很多周),第二行表示这 p 件装饰物的种类(可能出现相同的种类,即 ai = aj)。规定每件装饰物至少生产3 天,最多生产9 天。问每种装饰物需要生产的天数。如果没有解,则输出“Inconsistent data.”,如果有多解,则输出“Multiple solutions.”,如果只有唯一解,则输出每种装饰物需要生产的天数。
a1, a2......ap (1<= ai <= n)
第一行表示从星期 start 到星期 end 一共生产了p 件装饰物 (工作的天数为end - start + 1 + 7*x, 加 7*x 是因为它可能生产很多周),第二行表示这 p 件装饰物的种类(可能出现相同的种类,即 ai = aj)。规定每件装饰物至少生产3 天,最多生产9 天。问每种装饰物需要生产的天数。如果没有解,则输出“Inconsistent data.”,如果有多解,则输出“Multiple solutions.”,如果只有唯一解,则输出每种装饰物需要生产的天数。
思路:高斯消元接同模方程组
设每种装饰物需要生产的天数为 xi(1<=i<=n)。每一个条件就相当于给定了一个方程式,假设生产1 类装饰物 a1 件、2 类装饰物 a2 件、i 类装饰物 ai 件所花费的天数为 b = end - star + 1 + 7 * x,则可以列出下列方程:
a1 * x1 + a2 * x2 +...an * xn = b (mod 7)
这样一共可以列出m 个方程式,然后使用高斯消元来解此方程组即可。
a1 * x1 + a2 * x2 +...an * xn = b (mod 7)
这样一共可以列出m 个方程式,然后使用高斯消元来解此方程组即可。
代码:
1 #include <iostream> 2 #include <stdio.h> 3 #include <algorithm> 4 #include <string.h> 5 #include <math.h> 6 using namespace std; 7 8 const int MAXN = 4e2; 9 const int mod = 7; 10 bool free_x[MAXN]; 11 int a[MAXN][MAXN]; 12 int x[MAXN]; 13 14 inline int gcd(int a, int b){ 15 int t; 16 while(b != 0){ 17 t = b; 18 b = a % b; 19 a = t; 20 } 21 return a; 22 } 23 24 inline int lcm(int a, int b){ 25 return a / gcd(a, b) * b; 26 } 27 28 //返回-1表示无解,0表示有唯一解,大于0表示无穷解并返回变元个数 29 int Gauss(int equ, int var){//equ为方程数,var为未知数个数 30 int i, j, k; 31 int max_r;//当前列绝对值最大的行 32 int col;//当前处理的列 33 int ta, tb; 34 int LCM; 35 int temp; 36 int free_x_num; 37 int free_index; 38 for(int i = 0; i <= var; i++){ 39 x[i] = 0; 40 free_x[i] = true;//初始化全部为变元 41 } 42 for(k = 0, col = 0; k < equ && col < var; k++, col++){ 43 //枚举当前处理的行k 44 //找到当前col列元素绝对值最大的行与第k行交换 45 max_r = k; 46 for(i = k + 1; i < equ; i++){ 47 if(abs(a[i][col] > abs(a[max_r][col]))) max_r = i;//记录当前列中最大值所在行 48 } 49 if(max_r != k){//与第k行交换 50 for(int j = k; j < var + 1; j++){ 51 swap(a[k][j], a[max_r][j]); 52 } 53 } 54 if(a[k][col] == 0){//说明col列中第k行以下全是0了,则处理当前下一行 55 k--; 56 continue; 57 } 58 for(i = k + 1; i < equ; i++){//枚举要删去的行 59 if(a[i][col] != 0){ 60 LCM = lcm(abs(a[i][col]), abs(a[k][col])); 61 ta = LCM / abs(a[i][col]); 62 tb = LCM / abs(a[k][col]); 63 if(a[i][col] * a[k][col] < 0) tb = -tb; 64 for(j = col; j < var + 1; j++){ 65 a[i][j] = ((a[i][j] * ta - a[k][j] * tb) % mod + mod) % mod; 66 } 67 } 68 } 69 } 70 //无解的情况,化简的增广矩阵中存在(0,0,...1)这样的行(a!=0) 71 for(i = k; i < equ; i++){ 72 if(a[i][col] != 0) return -1; 73 } 74 //无穷解的情况: 在var * (var + 1)的增广阵中出现(0, 0, ..., 0)这样的行,即说明没有形成严格的上三角阵. 75 //且出现的行数即为自由变元的个数. 76 if(k < var){ 77 for(i = k - 1; i >= 0; i--){ 78 // 第i行一定不会是(0, 0, ..., 0)的情况,因为这样的行是在第k行到第equ行. 79 // 同样,第i行一定不会是(0, 0, ..., a), a != 0的情况,这样的无解的. 80 free_x_num = 0;//用于判断该行中的不确定的变元的个数,如果超过1个,则无法求解,它们仍然为不确定的变元. 81 for(j = 0; j < var; j++){ 82 if(a[i][j] && free_x[j]) free_x_num++, free_index = j; 83 } 84 if(free_x_num > 1) continue;//无法求解出确定的变元 85 // 说明就只有一个不确定的变元free_index,那么可以求解出该变元,且该变元是确定的 86 temp = a[i][var]; 87 for(j = 0; j < var; j++){ 88 if(a[i][j] != 0 && j != free_index) temp -= a[i][j] * x[j] % mod; 89 temp = (temp % mod + mod) % mod; 90 } 91 x[free_index] = (temp / a[i][free_index]) % mod;//求出该变元 92 free_x[free_index] = 0;//该变元已经确定,取消对应变元标记 93 } 94 return var - k;//自由变元有var-k个 95 } 96 //唯一解的情况: 在var * (var + 1)的增广阵中形成严格的上三角阵. 97 //计算出Xn-1, Xn-2 ... X0. 98 for(i = var - 1; i >= 0; i--){ 99 temp = a[i][var]; 100 for(j = i + 1; j < var; j++){ 101 if(a[i][j] != 0) temp -= a[i][j] * x[j]; 102 temp = (temp % mod + mod) % mod; 103 } 104 while(temp % a[i][i] != 0) temp += mod; 105 x[i] = (temp /a[i][i]) % mod; 106 } 107 return 0; 108 } 109 110 int tran(char *s){ 111 if(strcmp(s, "MON") == 0) return 1; 112 if(strcmp(s, "TUE") == 0) return 2; 113 if(strcmp(s, "WED") == 0) return 3; 114 if(strcmp(s, "THU") == 0) return 4; 115 if(strcmp(s, "FRI") == 0) return 5; 116 if(strcmp(s, "SAT") == 0) return 6; 117 return 7; 118 } 119 120 char s1[20], s2[20]; 121 122 int main(void){ 123 int n, m, k, t; 124 while(~scanf("%d%d", &n, &m)){ 125 if(n + m == 0) break; 126 memset(a, 0, sizeof(a)); 127 for(int i = 0; i < m; i++){ 128 scanf("%d%s%s", &k, s1, s2); 129 a[i][n] = ((tran(s2) - tran(s1) + 1) % mod + mod) % mod;//方程组的常数项 130 while(k--){ 131 scanf("%d", &t); 132 t--; 133 a[i][t]++; 134 if(a[i][t] >= mod) a[i][t] %= mod; 135 } 136 } 137 int cnt = Gauss(m, n);//m为条件数目即方程数 138 if(cnt == 0){ 139 for(int i = 0; i < n; i++){ 140 if(x[i] <= 2) x[i] += 7;//题意要求每件物品最少生产3天,最多生产9天 141 printf("%d ", x[i]); 142 } 143 puts(""); 144 }else if(cnt == -1) puts("Inconsistent data."); 145 else puts("Multiple solutions."); 146 } 147 return 0; 148 }
我就是我,颜色不一样的烟火 --- geloutingyu