1. 程式人生 > >POJ 2947-Widget Factory(高斯消元解同余方程式)

POJ 2947-Widget Factory(高斯消元解同余方程式)

free popu == += 3-9 sca -m stack art

題目地址:

id=2947">POJ 2947

題意:N種物品。M條記錄,接寫來M行,每行有K。Start,End,表述從星期Start到星期End,做了K件物品。接下來的K個數為物品的編號。

此題註意最後結果要調整到3-9之間。

思路:

非常easy想到高斯消元。

可是是帶同余方程式的高斯消元,開始建立關系的時候就要MOD 7
解此類方程式時最後求解的過程要用到擴展gcd的思想,舉個樣例,假設最後得到的矩陣為:
1 1 4
0 6 4
則6 * X2 % 7= 4 % 7 則相當於6 * X2 + 7 * Y = 4,利用擴展gcd則可求出X2為3,則第一個方程為
X1 * 1 % 7 + 1*3 % 7 = 4 % 7 則相當於 X1 + 7 * Y = 1 得到X1=1。

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <set>
#include <queue>
#include <stack>
#include <map>
using namespace std;
typedef long long LL;
const int inf=0x3f3f3f3f;
const double pi= acos(-1.0);
const double esp=1e-6;
const int MAXN=310;
int aug[MAXN][MAXN];<span style="background-color: rgb(255, 255, 255);">//增廣矩陣行數為m,分別為0到m-1,列數為n+1,分別為0到n.</span>
int x[MAXN];//解集
int free_num;
int m,n;//m個方程。n個變元
int gcd(int a,int b) {
    int r;
    while(b!=0) {
        r=b;
        b=a%b;
        a=r;
    }
    return a;
}
int lcm(int a,int b) {
    return a/gcd(a,b)*b;
}
/*void Debug(void)
{
    puts("");
    int i,j;
    for(i=0;i<m;i++){
        for(j=0;j<n+1;j++){
            cout << matrix[i][j] << " ";
        }
        cout << endl;
    }
    cout << endl;
}*/
int trans(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 if(strcmp(str,"SUN")==0) return 7;
}
// 高斯消元法解方程組(Gauss-Jordan elimination).(-1表示無解。
//0表示唯一解。大於0表示無窮解。並返回自由變元的個數)
int Gauss() {
    int i,j;
    int row,col,max_r;// 當前這列絕對值最大的行;
    int LCM;
    int ta,tb;
    int tmp;
    for(row=0,col=0; row<m&&col<n; row++,col++) {
        // 枚舉當前處理的行.
        // 找到該col列元素絕對值最大的那行與第row行交換.(為了在除法時減小誤差)
        max_r=row;
        for(i=row+1; i<m; i++) {
            if(abs(aug[i][col])>abs(aug[max_r][col]))
                max_r=i;
        }
        if(max_r!=row) {
            // 與第row行交換
            for(j=row; j<n+1; j++)
                swap(aug[row][j],aug[max_r][j]);
        }
        if(aug[row][col]==0) {
            // 說明該col列第row行下面全是0了,則處理當前行的下一列.
            row--;
            continue;
        }
        for(i=row+1; i<m; i++) {
            // 枚舉要刪去的行.
            if(aug[i][col]!=0) {
                LCM=lcm(abs(aug[i][col]),abs(aug[row][col]));
                ta=LCM/abs(aug[i][col]);
                tb=LCM/abs(aug[row][col]);
                if(aug[i][col]*aug[row][col]<0) tb=-tb;//異號的情況是相加
                for(j=col; j<n+1; j++) {
                    aug[i][j]=(((aug[i][j]*ta-aug[row][j]*tb)%7+7)%7);
                }
            }
        }
    }
    //Debug();
    // 1. 無解的情況: 化簡的增廣陣中存在(0, 0, ..., a)這種行(a != 0).
    for(i=row; i<m; i++) {
        if(aug[i][col]!=0)  return -1;
    }
    // 2. 無窮解的情況: 在n * (n + 1)的增廣陣中出現(0, 0, ..., 0)這種行,即說明沒有形成嚴格的上三角陣.
    // 且出現的行數即為自由變元的個數.
    if(row<n){
        return n-row;
    }
     // 3. 唯一解的情況: 在n * (n + 1)的增廣陣中形成嚴格的上三角陣.
    // 計算出Xn-1, Xn-2 ... X0.
    for(i=n-1; i>=0; i--) {
        tmp=aug[i][n];//等式右邊的數
        for(j=i+1; j<n; j++) {
            if(aug[i][j]!=0) tmp-=aug[i][j]*x[j];//把已知的解帶入。減去,僅僅剩下,一個未知的解
            tmp=(tmp%7+7)%7;
        }
        while(tmp%aug[i][i]!=0)//外層每次循環都是為了求 a[i][i],由於它是每一個方程中唯一一個未知的變量(求該方程時)
            tmp+=7;//由於天數不確定,而aug[i][i]必須得為整數才幹夠,周期為7
        x[i]=(tmp/aug[i][i])%7;
    }
    return 0;
}
int main(void) {
    int nn,mm,i,j,k;
    int num;
    char Start[5],End[5];
    while(~scanf("%d %d",&nn,&mm)) {
        if(nn==0&&mm==0) break;
        n=m=0;
        memset(aug,0,sizeof(aug));
        for(i=0; i<mm; i++) {
            scanf("%d",&k);
            scanf("%s %s",Start,End);
            aug[i][nn]=((trans(End)-trans(Start)+1)%7+7)%7;
            for(j=1; j<=k; j++) {
                scanf("%d",&num);
                num--;
                aug[i][num]++;
                aug[i][num]%=7;//有反復的
            }
        }
        m=mm;
        n=nn;
        free_num = Gauss();
        if(free_num==0) {
            for(i=0; i<n; i++)
                if(x[i]<3)//依據題意,每一個零件的加工時間在3-9天.
                    x[i]+=7;
            for(i=0; i<n-1; i++)
                printf("%d ",x[i]);
            printf("%d\n",x[i]);
        } else if(free_num==-1)
            puts("Inconsistent data.");
        else
            puts("Multiple solutions.");
    }
    return 0;
}



POJ 2947-Widget Factory(高斯消元解同余方程式)