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\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;
}
时间: 2024-10-18 22:06:05

gauss消元的相关文章

hdu 5755(GAuss 消元)

Gambler Bo Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 1152    Accepted Submission(s): 471Special Judge Problem Description Gambler Bo is very proficient in a matrix game. You have a N×M m

Gauss 消元(模板)

1 /* 2 title:Gauss消元整数解/小数解整数矩阵模板 3 author:lhk 4 time: 2016.9.11 5 没学vim的菜鸡自己手打了 6 */ 7 #include<cstdio> 8 #include<iostream> 9 #include<cstring> 10 #include<algorithm> 11 #include<cmath> 12 #define clr(x) memset(x,0,sizeof(x

求一个n元一次方程的解,Gauss消元

const Matrix=require('./Matrix.js') /*Gauss 消元 传入一个矩阵,传出结果 */ function Gauss(matrix){ let l=[];//是否为自由元 let ans=[];//存储解 const n=matrix.Column-1;//解的个数 const EPS=0.00001; let res=0,r=0; for(let i=0;i<matrix.Column;i++){ for(let j=r;j<matrix.Row;j++)

POJ1830开关问题——gauss消元

题目链接 分析: 第一个高斯消元题目,操作是异或.奇偶能够用0.1来表示,也就表示成bool类型的方程,操作是异或.和加法没有差别 题目中有两个未知量:每一个开关被按下的次数(0.1).每一个开关的转换次数. 题目仅仅和操作次数的奇偶有关,所以用0.1表示之后,对于每一个开关的转换次数就已经知道了.所以仅仅有一个未知量.能够线性表示.练习使用模板 const int maxn = 40; int a[maxn][maxn]; int gauss(int N, int M) { int r, c,

51nod1446 Kirchhoff矩阵+Gauss消元+容斥+折半DFS

思路: //By SiriusRen #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int mod=1000000007; int cases,n,maxval,a[44][44],C[44][44],f[44],val[44],X,g[44]; int top2,top,bgn,half,cnts2[2

【模板】高斯消元

Gauss消元,我在线代书上学会的-- 大概就是每次把每行第一个元素消掉,直到成为上三角矩阵为止. 此时从最后一个元素反代回去,就可以求出线性方程组的解. #include<bits/stdc++.h> #define N 205 using namespace std; const double eps=1e-8; int n; double a[N][N],del; bool gauss(){ for(int i=1;i<=n;i++){ int k=i; for(int j=i+1

【高斯消元】BZOJ 1013: [JSOI2008]球形空间产生器sphere

Description 有一个球形空间产生器能够在n维空间中产生一个坚硬的球体.现在,你被困在了这个n维球体中,你只知道球面上n+1个点的坐标,你需要以最快的速度确定这个n维球体的球心坐标,以便于摧毁这个球形空间产生器. Input 第一行是一个整数,n.接下来的n+1行,每行有n个实数,表示球面上一点的n维坐标.每一个实数精确到小数点后6位,且其绝对值都不超过20000. Output 有且只有一行,依次给出球心的n维坐标(n个实数),两个实数之间用一个空格隔开.每个实数精确到小数点后3位.数

题解 外星千足虫(线性基+高斯消元)

题解 luogu外星千足虫(线性基+高斯消元) 题目 luogu题目传送门 题解想法 首先需要知道这是个异或方程对吧 然后既然看到位运算,又有这么多,就可以考虑线性基(做题技巧),那我们就丢进去 接下来看一看线性基,哇,性质美妙 它不就是Gauss消元里面想要的上三角矩阵吗 所以说: 如果能拼成线性基,那么枚举到哪里完成了,就输出位置(first_ans) 如果拼不成,那就解不出(毋庸置疑) 那真是美妙啊... 所以怎么消元呢?这可是个异或方程,我们要解出来啊 枚举整个线性基的g[i] 如果g[

poj_1222_高斯消元

第一次学习使用高斯消元,将灯板化为线性方程组,进行求解. /*######################################################################### # File Name: poj_1222.cpp # Author: CaoLei # Created Time: 2015/7/20 15:48:04 ###################################################################