• gauss消元


    题意描述:有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
    " ); 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
    ", tmp );
        }
        return 0;
    }
    
  • 相关阅读:
    thinkphp简洁、美观、靠谱的分页类
    查询文章的上下篇Sql语句
    人类阅读的优越方式打印php数组
    弹出遮罩层后,如何禁止底层页面的滚动
    解决PHP Redis扩展无法加载的问题(zend_new_interned_string in Unknown on line 0)
    PHP Warning: PHP Startup: redis: Unable to initialize module Windows版本phpredis扩展
    带你使用JS-SDK自定义微信分享效果
    lnmp环境切换php版本,并安装相应redis扩展
    最新git源码下载地址
    微信小程序之发送模板消息(通过openid推送消息给用户)
  • 原文地址:https://www.cnblogs.com/hefenghhhh/p/4766186.html
Copyright © 2020-2023  润新知