本質上講,這兩道題都是數學題,ratios是行列式解線性方程組,squares是組合數學。可見ACMer數學基礎好确實是非常重要的。
1、Feed Ratios
所求的實際上就是一個最簡配比 x, y, z, w,使得 x(a1,b1,c1) + y(a2,b2,c2) + z(a3,b3,c3) = w(g1,g2,g3),我線性代數也忘得差不多了,不過對克萊姆法則還有一點印象。
- #include <fstream>
- using namespace std ;
- ifstream fin ( "ratios.in" ) ;
- ofstream fout ( "ratios.out" ) ;
- const int LEN = 3 ;
- int dest [LEN ] ;
- int matrix [LEN ] [LEN ], backup [LEN ] [LEN ] ;
- void cofactor ( const int a [ ] [LEN ], int b [ ] [LEN ], int n, int k )
- {
- int bi = 0, bj = 0 ;
- for ( int i = 1 ; i < n ; ++i )
- {
- for ( int j = 0 ; j < n ; ++j ) {
- if (j ==k )
- continue ;
- b [bi ] [bj ] = a [i ] [j ] ;
- bj ++ ;
- }
- bi ++ ;
- bj = 0 ;
- }
- }
- int deter ( int a [ ] [LEN ], int n )
- {
- if (n == 1 )
- return a [ 0 ] [ 0 ] ;
- else
- {
- int sum = 0 ;
- for ( int i = 0 ; i < n ; ++i )
- {
- int b [LEN ] [LEN ] = { 0 } ;
- cofactor (a,b,n,i ) ;
- int coef = ( (i % 2 == 0 ) ? 1 : - 1 ) ;
- sum + = (coef *a [ 0 ] [i ] *deter (b,n - 1 ) ) ;
- }
- return sum ;
- }
- }
- void change ( int curr, int last = - 1 )
- {
- if (last ! = - 1 )
- {
- for ( int i = 0 ; i < LEN ; ++i )
- matrix [last ] [i ] = backup [last ] [i ] ;
- }
- for ( int i = 0 ; i < LEN ; ++i )
- matrix [curr ] [i ] = dest [i ] ;
- }
- int gcd ( int m, int n )
- {
- if (m == 0 ) return n ;
- if (n == 0 ) return m ;
- if (m < n )
- {
- int tmp = m ;
- m = n ;
- n = tmp ;
- }
- while (n ! = 0 )
- {
- int tmp = m % n ;
- m = n ;
- n = tmp ;
- }
- return m ;
- }
- int main ( )
- {
- for ( int i = 0 ; i < LEN ; i ++ )
- fin >> dest [i ] ;
- int num ;
- for ( int i = 0 ; i < 3 ; i ++ )
- {
- for ( int j = 0 ; j < 3 ; j ++ )
- {
- fin >> num ;
- matrix [i ] [j ] = num, backup [i ] [j ] = num ;
- }
- }
- int res1 = deter (matrix ,LEN ) ;
- if (res1 == 0 ) {
- fout << "NONE\n" ;
- return 0 ;
- }
- change ( 0, - 1 ) ;
- int res2 = deter (matrix, LEN ) ;
- change ( 1, 0 ) ;
- int res3 = deter (matrix, LEN ) ;
- change ( 2, 1 ) ;
- int res4 = deter (matrix, LEN ) ;
- if (res1 < 0 )
- {
- res1 = -res1 ;
- res2 = -res2 ;
- res3 = -res3 ;
- res4 = -res4 ;
- }
- if (res2 < 0 || res3 < 0 || res4 < 0 )
- {
- fout << "NONE\n" ;
- return 0 ;
- }
- int tmp = gcd (gcd (res2, gcd (res3, res4 ) ), res1 ) ;
- fout << res2 /tmp << ' ' << res3 /tmp << ' ' << res4 /tmp << ' ' << res1 /tmp << '\n' ;
- return 0 ;
- }
值得一提的是求解行列式的值,也是個比較好的遞歸的示例吧,我想大學裡的《線性代數》課完全可以用程式設計來教嘛...
2、Magic Squares
這題還是蠻好的,整體思路要上考慮BFS,然後一個比較難的地方是設計一個好的HASH函數,因為BFS通常是很耗費空間的,要是HASH函數還沒有比較好的空間使用率,可能會掉鍊子,當然這道題資料量不大,8位數位闆,總共也就8! = 4W多種情況,但是你是程式員,多少得精益求精一點,是以,有了康托展開定理。
設Sn = {1,2,3,4,...,n}表示1,2,3,...,n的排列,如S3 = {1,2,3} 按從小到大排列一共6個 123 132 213 231 312 321。那麼找出S5中45231在這個排列中所在的位置可以用下面的思路:
- 比4小的數有3個
- 比5小的數有4個但4已經在之前出現過了是以是3個
- 比2小的數有1個
- 比3小的數有兩個但2已經在之前出現過了是以是1個
- 比1小的數有0個
- 那麼45231在這個排列中的順序是3*4!+3*3!+1*2!+1*1!+0*0!+1=94
空間還是可以的,時間表現一般吧,廣搜是一般的思路,有誰能想出來啟發式算法嗎?
- #include <fstream>
- #include <vector>
- #include <deque>
- #include <string>
- using namespace std ;
- ifstream fin ( "msquare.in" ) ;
- ofstream fout ( "msquare.out" ) ;
- typedef vector < int > VInt ;
- typedef void Fun ( const VInt &, VInt & ) ;
- typedef Fun * FunPtr ;
- const int MAX_NUM = 40321 ;
- const int MSQUARE_SIZE = 8 ;
- const int FRAC [MSQUARE_SIZE ] = { 1, 1, 2, 6, 24, 120, 720, 5040 } ;
- const string sFlag = "ABC" ;
- bool HashTable [MAX_NUM ] = { 0 } ;
- struct msquare
- {
- msquare ( ) : vState (MSQUARE_SIZE ), sPath ( ) { }
- VInt vState ;
- string sPath ;
- } ;
- deque <msquare > Q ;
- void SwapRow ( const VInt & vSrc, VInt & vRes )
- {
- vRes. resize ( 8 ) ;
- int i, j = 0 ;
- for (i = 4 ; i < 8 ; ++i, ++j )
- vRes [j ] = vSrc [i ] ;
- for (i = 0 ; i < 4 ; ++i, ++j )
- vRes [j ] = vSrc [i ] ;
- }
- void SwapColumn ( const VInt & vSrc, VInt & vRes )
- {
- vRes. resize ( 8 ) ;
- vRes [ 0 ] = vSrc [ 3 ] ;
- for ( int i = 1 ; i < 4 ; ++i )
- vRes [i ] = vSrc [i - 1 ] ;
- vRes [ 4 ] = vSrc [ 7 ] ;
- for ( int i = 5 ; i < 8 ; ++i )
- vRes [i ] = vSrc [i - 1 ] ;
- }
- void RotateMiddle ( const VInt & vSrc, VInt & vRes )
- {
- vRes = vSrc ;
- vRes [ 1 ] = vSrc [ 5 ] ;
- vRes [ 2 ] = vSrc [ 1 ] ;
- vRes [ 5 ] = vSrc [ 6 ] ;
- vRes [ 6 ] = vSrc [ 2 ] ;
- }
- inline int Contor (VInt & v )
- {
- int ans = 0 ;
- for ( int i = 0 ; i < 8 ; i ++ )
- {
- int tmp = 0 ;
- for ( int j = i + 1 ; j < 8 ; j ++ ) if (v [i ] > v [j ] ) ++tmp ;
- ans + = tmp * FRAC [ 7 -i ] ;
- }
- return ans + 1 ;
- }
- int main ( )
- {
- VInt vDest (MSQUARE_SIZE ), vStart (MSQUARE_SIZE ) ;
- for ( int i = 0 ; i < 4 ; ++i )
- fin >> vDest [i ] ;
- for ( int i = 7 ; i >= 4 ; --i )
- fin >> vDest [i ] ;
- for ( int i = 0 ; i < 4 ; ++i )
- vStart [i ] = i + 1 ;
- for ( int i = 7, j = 4 ; i >= 4 ; --i, ++j )
- vStart [j ] = i + 1 ;
- if (vStart == vDest )
- {
- fout << 0 << endl << endl ;
- return 0 ;
- }
- FunPtr funArr [ 3 ] ;
- funArr [ 0 ] = SwapRow, funArr [ 1 ] = SwapColumn, funArr [ 2 ] = RotateMiddle ;
- msquare m ;
- m. vState = vStart ;
- Q. push_back (m ) ;
- VInt vTmp ;
- int HashValue = Contor (vStart ) ;
- HashTable [HashValue ] = true ;
- while ( !Q. empty ( ) )
- {
- for ( int i = 0 ; i < 3 ; ++i )
- {
- m = Q. front ( ) ;
- vTmp. clear ( ) ;
- funArr [i ] (m. vState, vTmp ) ;
- if (vTmp == vDest )
- {
- m. sPath + = sFlag [i ] ;
- size_t size = m. sPath. size ( ), start = 0, len ;
- fout << size << endl ;
- while ( true )
- {
- if (size > 60 )
- {
- len = 60 ;
- start + = len ;
- size - = len ;
- fout << string (m. sPath, start, len ) << endl ;
- }
- else
- {
- len = size ;
- fout << string (m. sPath, start, len ) << endl ;
- return 0 ;
- }
- }
- }
- HashValue = Contor (vTmp ) ;
- if ( !HashTable [HashValue ] )
- {
- m. vState = vTmp ;
- m. sPath + = sFlag [i ] ;
- Q. push_back (m ) ;
- HashTable [HashValue ] = true ;
- }
- }
- Q. pop_front ( ) ;
- }
- return 0 ;
- }