码迷,mamicode.com
首页 > 其他好文 > 详细

gauss消元

时间:2015-08-28 15:00:49      阅读:173      评论:0      收藏:0      [点我收藏+]

标签:

题意描述:有n个星球,m台望远镜。每台望远镜有一个开始时间和结束时间,但只给出了月、日的信息,没有给出年份,每台望远镜记录了它所观测的星球上发生的各类事件的次数。每类事件持续的时间是恒定的,且不会超过365天,不管在哪个星球上发生。告诉你每台望远镜的起止时间,和它观测到的各类时间发生的次数。问每类事件持续多长时间?可能有多个解,输出一个可行解即可。

数据范围:n<200,m<200

分析:设第i类时间持续的时间为xi,第i个望远镜的观测时间长度为Ci,它观测到的各类事件发生的次数为fi,则第i个望远镜可以列出以下方程:

∑fi*xi%365=Ci

于是可以得到一个模线性方程组。但是因为365不是质数,不能直接利用高斯-亚当消元法解这个方程组。为什么呢?

因为高斯亚当消元要对方程进行线性变换,比如乘上某个系数。但是乘的系数与模不互质的话,则变换前后两个方程不是等价的,可能造成解集扩大。

比如方程A:2x%15=1

方程A乘上系数3,得到方程B: 6x%15=3。

方程A和B不是等价的。满足A的解一定满足B,但是满足B的解不一定满足A。或者说方程A可以得到B,但方程B不能得到A。

所以对模型线性方程组要进行线性变换一定要保证模是质数。

那么接下来怎么做呢?对365进行质因数分解:365=73*5.

于是由方程∑fi*xi%365=Ci可以得到两个方程:

∑fi*xi%73=Ci%73

∑fi*xi%5=Ci%5

于是原来的方程组可以化成两个方程组。对其分别求解,设方程1的解集为X1,方程2的解集为X2。

则可以由X1和X2构造出一个解X,同时满足两个方程,即利用中国剩余定理.

代码如下:

/*
  Sluzbeno rjesenje zadatka Planete. Trebalo bi dobiti 100% bodova. 
  Slozenost algoritma: O( N^2*F )
  Autor: Goran Zuzic
 */


#include <algorithm>
#include <functional>

#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <vector>
#include <string>

using namespace std;

const int MAXN = 210;
const int MAXF = 210;

int N, F;
int DuM[ 12 ] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

int Init[ MAXN ][ MAXF+1 ];
int Mat[ MAXN ][ MAXF+1 ];

inline int get_day() 
{
    int d, m; scanf( "%d %d", &d, &m ); --d, --m;

    for( int i = 0; i < m; ++i )
        d += DuM[i];

    return d;
}

int Sol5[ MAXF ];
int Sol73[ MAXF ];

int Inverz[ 100 ];
int superOk = true;

void gauss_solve( int p, int *Sol )
{
    for( int i = 0; i < N; ++i )
        for( int j = 0; j <= F; ++j )
            Mat[i][j] = Init[i][j] % p;
    Inverz[0] = 0;
    for( int i = 1; i < p; ++i )
        for( int j = 1; j < p; ++j )
            if( (i*j)%p == 1 )
                Inverz[i] = j;
    int R = 0;
    for( int s = 0; s < F; ++s ) {
        int indeks = -1;
        for( int i = R; indeks == -1 && i < N; ++i ) 
            if( Mat[i][s] != 0 )
                indeks = i; //indeks是该列不为0的所有行的第一行
        if( indeks == -1 ) continue;//该列为0
        if( R != indeks )//第R行和第i行交换
            for( int i = 0; i <= F; ++i )
                swap( Mat[R][i], Mat[indeks][i] );
        int mnozi = Inverz[ Mat[R][s] ];//求系数的逆元,作为倍数。
        for( int i = 0; i <= F; ++i )
            Mat[R][i] = ( Mat[R][i]*mnozi ) % p;
        for( int i = 0; i < N; ++i )
            if( i != R ) {
                int coef = Mat[i][s];
                for( int j = 0; j <= F; ++j ) {
                    Mat[i][j] -= coef * Mat[R][j];
                    Mat[i][j] %= p; if( Mat[i][j] < 0 ) Mat[i][j] += p;
                }
            }
        ++R;
    }
    for( int i = 0; i < N; ++i ) {
        int first = -1;
        for( int j = 0; j < F; ++j )
            if( Mat[i][j] != 0 ) {
                first = j;
                break;
            }

        if( first == -1 ) {
            if( Mat[i][F] != 0 ) { superOk = false; return ; }            
            continue;
        }
        Sol[ first ] = Mat[i][F];
    }
}

int main( void )
{
    scanf( "%d %d", &N, &F );
    for( int i = 0; i < N; ++i ) {
        int a = get_day();
        int b = get_day();
        for( int j = 0; j < F; ++j )
            scanf( "%d", Init[i] + j );
        Init[i][F] = ((b-a)%365+365)%365;
    }

    gauss_solve( 5, Sol5 );
    gauss_solve( 73, Sol73 );

    if( !superOk ) { printf( "-1\n" ); return 0; }

    for( int i = 0; i < F; ++i ) {
        int tmp = ( 146*Sol5[i] + 220*Sol73[i] ) % 365;
        if( tmp == 0 ) tmp = 365;
        printf( "%d\n", tmp );
    }
    return 0;
}

 

gauss消元

标签:

原文地址:http://www.cnblogs.com/hefenghhhh/p/4766186.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!