poj 2947 Widget Factory(高斯消元)
题意:p strat end ,表示员工i开始工作的星期,以及被解雇的星期,和在这期间他共加工了p种首饰,下一行是他都加工了哪几种首饰,问你通过这些记录是否能得出加工每种首饰需要多少天。
思路:典型的高斯消元,设每种首饰需要的天数为xi,这个的每个记录就是一个方程,所以用高斯消元法解这个方程组就可以了,需要注意的是,他给出的是一个星期中的某一天,但他做了几个星期不知道,a1*x1 + a2 * x2 + .....=( b + 7 * x ) % 7 ;在本题中要注意%7 ,嗯,每个地方都要摸,不能超出7。
代码:
View Code
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <iostream> #include <algorithm> #include <queue> #include <math.h> #define N 305 using namespace std ; int val[N][N] , d[N] ; int n , m ; //转化日期 int chang ( char str[] ) { if ( strcmp ( str , "MON" ) == 0 ) return 1 ; else if ( strcmp ( str , "TUE" ) == 0 ) return 2 ; else if ( strcmp ( str , "WED" ) == 0 ) return 3 ; else if ( strcmp ( str , "THU" ) == 0 ) return 4 ; else if ( strcmp ( str , "FRI" ) == 0 ) return 5 ; else if ( strcmp ( str , "SAT" ) == 0 ) return 6 ; else return 7 ; } //高斯消元 void solve() { int row , col , i , j , k , t , tem ; for ( row = 0 , col = 0 ; row < m && col < n ; col++ ) { //寻找交换行 for ( i = row ; i < m ; i++ ) if ( val[i][col] ) break ; if ( val[i][col] ) { //交换两行 for ( j = col ; j <= n ; j++ ) { tem = val[i][j] ; val[i][j] = val[row][j] ; val[row][j] = tem ; } //用每一行与这一行做减法 for ( j = 0 ; j < m ; j++ ) if ( j != row && val[j][col] ) { int ta = val[j][col] ; int tb = val[row][col] ; for ( k = 0 ; k <= n ; k++ ) val[j][k] = (( val[j][k] * tb - val[row][k] * ta ) % 7 + 7 ) % 7 ; } row++ ; } } //无解 for ( i = row ; i < m ; i++ ) if ( val[i][n] != 0 ) { printf ( "Inconsistent data.\n" ); return ; } //无穷解 if ( row < n ) { printf ( "Multiple solutions.\n" ); return ; } //唯一解 for ( i = n - 1 ; i >= 0 ; i-- ) { tem = val[i][n] ; for ( j = i + 1 ; j < n ; j++ ) tem = ( ( tem - val[i][j] * d[j] ) %7 + 7 ) % 7 ; while ( tem % val[i][i] != 0 ) tem += 7 ; d[i] = ( tem / val[i][i] ) % 7 ; } //每种首饰的加工时间在3-9天 for ( i = 0 ; i < n ; i++) if ( d[i] < 3 ) d[i] += 7 ; for ( i = 0 ; i < n ; i++ ) { if ( i ) printf ( " %d" , d[i] ); else printf ( "%d" , d[i] ); } printf ( "\n" ); return ; } int main() { int i , j , x , dx , date ; char s[10] , t[10] ; while ( scanf ( "%d%d" , &n , &m ) != EOF ) { if ( !( n + m )) break; memset( val , 0 , sizeof ( val )); memset( d , 0 , sizeof ( d )); for ( i = 0 ; i < m ; i++ ) { //getchar(); scanf ( "%d %s %s" , &x , s , t ); date = ( chang( t ) - chang( s ) + 1 + 7 ) % 7 ; val[i][n] = date ; for ( j = 0 ; j < x ; j++ ) { scanf ( "%d" , &dx ); val[i][dx-1]++ ; } for( j = 0 ; j < n ; j++ ) val[i][j] %= 7 ; } solve( ) ; } return 0 ; }
还想说一下高斯消元,其实就是利用矩阵求线性方程的解,这个在线性代数中有讲到,我也是又翻了下课本才想起来的。下面给出一个模板,每一步都讲得很详细。
代码:
View Code
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <iostream> #include <algorithm> #include <queue> #include <math.h> #define N 1002 using namespace std ; int data[N][N] , vis[N] , x[N] ; int n , m ; void print() { int i , j ; for ( i = 0 ; i < n ; i++ ) { for ( j = 0 ; j < m + 1 ; j++ ) { if ( j ) printf ( " %d" , data[i][j] ); else printf ( "%d" , data[i][j] ); } printf ( "\n" ); } printf ( "\n" ); } int gcd ( int x , int y ) { if ( !y ) return x ; else return gcd ( y , x % y ); } int lcm ( int x , int y ) { return x * y / gcd ( x , y ) ; } //-2表示有浮点数解,但无整数解,-1表示无解,0表示唯一解, //大于0表示无穷解,并返回自由变元的个数 int Gauss ( ) { int col , row , i , j , k , tem , tmax ; // tmax表示当前这列绝对值最大的行. //row 当前处理的行, for ( col = 0 , row = 0 ; row < n && col < m ; col++ , row++ ) { tmax = row ;//寻找当前列中绝对值最大的那行与之交换,为了在除法时减小误差 for( i = row + 1 ; i < n ; i++ ) if ( abs ( data[i][col] ) > abs ( data[tmax][col] )) tmax = i ; //cout<<data[tmax][col]<<endl; //交换两行元素 if ( tmax != row ) { for ( j = row ; j < m + 1 ; j++ ) { tem = data[tmax][j] ; data[tmax][j] = data[row][j] ; data[row][j] = tem ; } } //说明该col列第row行以下全是0了,则处理当前行的下一列. if ( data[row][col] == 0 ) { row-- ; continue ; } //每一行都与当前行进行减法运算,使得与它相减的当前列的元素为0 for ( i = row + 1 ; i < n ; i++ ) if ( data[i][col] ) { //寻找最小公倍数 int tx = lcm ( abs( data[i][col] ) , abs( data[row][col] )) ; int ta = tx / abs( data[i][col] ) ; int tb = tx / abs( data[row][col] ) ; if ( data[i][col] * data[row][col] < 0 ) ta = -ta ;// 异号的情况下是两个数相加. for ( j = col ; j < m + 1 ; j++ ) data[i][j] = data[i][j] * ta - data[row][j] * tb ; } cout<<endl; print();//打印出每个变化后的矩阵 } // 1. 无解的情况: 化简的增广阵中存在(0, 0, ..., a)这样的行(a != 0). for( i = row ; i < n ; i++ ) if ( data[i][col] != 0 ) return -1 ; // 2. 无穷解的情况: 在m * ( m + 1)的增广阵中出现(0, 0, ..., 0)这样的行,即说明没有形成严格的上三角阵. // 且出现的行数即为自由变元的个数. if ( row < m ) { // 首先,自由变元有 m - row 个,即不确定的变元至少有var - k个 // 第i行一定不会是(0, 0, ..., 0)的情况,因为这样的行是在第row行到第n行. // 同样,第i行一定不会是(0, 0, ..., a), a != 0的情况,这样的无解的. for ( i = row - 1 ; i >= 0 ; i-- ) { int num = 0 ; // 用于判断该行中的不确定的变元的个数,如果超过1个,则无法求解,它们仍然为不确定的变元. int pos ; for ( j = 0 ; j < m + 1 ; j++ ) if ( data[i][j] && !vis[j] ) { num++ ;pos = j ; } if ( num > 1 ) continue ; // 说明就只有一个不确定的变元pos,那么可以求解出该变元,且该变元是确定的. tem = data[i][m] ; for ( j = 0 ; j < m ; j++ ) if ( data[i][j] && j != pos ) tem -= data[i][j] * x[j] ; x[pos] = tem / data[i][pos] ; vis[pos] = 1 ; } return m - i ; } // 3. 唯一解的情况: 在m * ( m + 1)的增广阵中形成严格的上三角阵. for ( i = m - 1 ; i >= 0 ; i-- ) { tem = data[i][m] ; for ( j = i + 1 ; j < m ; j++ ) if( data[i][j] ) tem -= data[i][j] * x[j] ; if ( tem % data[i][i] ) return -2 ;//有浮点数解 x[i] = tem / data[i][i] ; vis[i] = 1 ; } return 0 ; } int main() { int flag , i , j ; //freopen("input.txt" , "r" , stdin ); while ( scanf ( "%d%d" , &n , &m ) != EOF ) { memset ( data , 0 , sizeof ( data )); memset ( vis , 0 , sizeof ( vis )); //输入矩阵元素 for ( i = 0 ; i < n ; i++ ) { for ( j = 0 ; j < m + 1 ; j++ ) scanf ( "%d" , &data[i][j] ); } flag = Gauss () ; if ( flag == -1 ) { printf ( "无解\n" ); } else if ( flag == -2 ) { printf ( "有浮点数解\n" ) ; } else if ( flag > 0 ) { printf ( "有无穷解\n" ) ; } else { for ( i = 0 ; i < m ; i++ ) { printf ( "x%d = %d\n" , i + 1 , x[i] ); } //printf ( "\n" ); } } return 0 ; }