POJ 2947 Widget Factory (高斯消元)

Description

The widget factory produces several different kinds of widgets. Each widget is carefully built by a skilled widgeteer. The time required to build a widget depends on its type: the simple widgets need only 3 days, but the most complex ones may need as many as 9 days.

The factory is currently in a state of complete chaos: recently, the factory has been bought by a new owner, and the new director has fired almost everyone. The new staff know almost nothing about building widgets, and it seems that no one remembers how many days are required to build each diofferent type of widget. This is very embarrassing when a client orders widgets and the factory cannot tell the client how many days are needed to produce the required goods. Fortunately, there are records that say for each widgeteer the date when he started working at the factory, the date when he was fired and what types of widgets he built. The problem is that the record does not say the exact date of starting and leaving the job, only the day of the week. Nevertheless, even this information might be helpful in certain cases: for example, if a widgeteer started working on a Tuesday, built a Type 41 widget, and was fired on a Friday,then we know that it takes 4 days to build a Type 41 widget. Your task is to figure out from these records (if possible) the number of days that are required to build the different types of widgets.

Input

The input contains several blocks of test cases. Each case begins with a line containing two integers: the number 1 ≤ n ≤ 300 of the different types, and the number 1 ≤ m ≤ 300 of the records. This line is followed by a description of the m records. Each record is described by two lines. The first line contains the total number 1 ≤ k ≤ 10000 of widgets built by this widgeteer, followed by the day of week when he/she started working and the day of the week he/she was fired. The days of the week are given bythe strings `MON', `TUE', `WED', `THU', `FRI', `SAT' and `SUN'. The second line contains k integers separated by spaces. These numbers are between 1 and n , and they describe the diofferent types of widgets that the widgeteer built. For example, the following two lines mean that the widgeteer started working on a Wednesday, built a Type 13 widget, a Type 18 widget, a Type 1 widget, again a Type 13 widget,and was fired on a Sunday.

4 WED SUN
13 18 1 13

Note that the widgeteers work 7 days a week, and they were working on every day between their first and last day at the factory (if you like weekends and holidays, then do not become a widgeteer!).

The input is terminated by a test case with n = m = 0 .

Output

For each test case, you have to output a single line containing n integers separated by spaces: the number of days required to build the different types of widgets. There should be no space before the first number or after the last number, and there should be exactly one space between two numbers. If there is more than one possible solution for the problem, then write `Multiple solutions.' (without the quotes). If you are sure that there is no solution consistent with the input, then write `Inconsistent data.'(without the quotes).

Sample Input

2 3
2 MON THU
1 2
3 MON FRI
1 1 2
3 MON SUN
1 2 2
10 2
1 MON TUE 
3
1 MON WED
3
0 0

Sample Output

8 3
Inconsistent data.

Hint

Huge input file, 'scanf' recommended to avoid TLE.
 
题意:
一个小部件生产厂生产各种各样的小部件,并且每一个小部件的生产由一个技工负责,生产一个小部件的时间取决于它本身,简单的用3天,复杂的最多用9天。这个工厂最近很混乱,因为厂子被另一个人收购了,并且解雇了所有的老员工,而新员工对部件的生产一无所知,更不知道生产每种小部件到底用多少时间,所以当客户来下单但厂子不能告诉客户要等多少天时是很尴尬的一件事。幸运的是档案留下了,里面记录着每个技工开始工作的时间,被解雇的时间和他们所生产的部件的种类编号,你的任务就是计算出生产每种部件所需要的时间。
 
输入包含多组测试实例。每个测试实例第一行输入n,m (n是部件的总种类数,m是档案的个数即技工的个数);接下来m个档案描述,每个描述两行,第一行给出该技工所生产的部件的总数k,开始工作的时间,被解雇的时间,第二行k个数表示这k个部件都属于哪个种类(1=<产品编号<=n)。每周工作7天不休假。当n=m=0时结束。
 
若有唯一解,则按产品编号顺序输出每种部件所用的时间,数字之间用一个空格隔开。如果有多组解就输出Multiple solutions. 如果没有解就输出Inconsistent data.
 
 
思路:

高斯消元解同余方程组,因为是同模一个数,原因是所给出的时间只是每周的星期数,所以我们要模7。实际上就类似于普通的同余高斯方程解方程。对于一条记录,可以列出一条方程(a1x1+a2x2+...+anxn) mod 7 = k 这里需要注意的是系数需要模下7,否则会溢出。然后就是从第一行往下代入消元了,因为是求整数解,所以求一下两个方程要消元的最小公倍数然后再求,消完元后需要重新模7。理所应当的,如果消元过程中出现之下所有式子某项系数都为0,该项未知数就是自由元,但这并不能说明有多解……

这道题比较麻烦的就是区分多解和无解,若m>n则有一些式子是多余的,他们可能造成无解,所以高斯消元消完后应当验证多余式子是否系数全为0,等式右边是否为0

若存在自由元,将会造成一些式子变成多余的式子,我的方法是先求解,然后倒着带入验证

消成 ax mod 7 = k 的时候,枚举一下x,可能会有多解或无解……这个没注意过是否会有这种情况,但还是判断了一下

求解

|A1X1+A2X2+...+AnXn% 7 = k1

|B1X1+B2X2+...+BnXn% 7 = k2

| ...

|M1X1+M2X2+...+MnXn% 7 = km

问这个方程组有没有解?

如果去掉mod7就是一个很赤裸的高斯消元,不过加了以后只要稍微变化下就可以了

我们只要在高斯消元后,最后得出解时把解的值枚举下看看,看看是否有解在去摸后和结果相同

值得注意的几点:

1 ax前面系数取模对结果没有影响,所以防止溢出还是取吧。。

2 最后枚举时候模要正的

3 方程数和系数不对称

我们用到系数矩阵和增广矩阵:(1)当系数矩阵的秩不等于增广矩阵的秩时就是无解 (2)当系数矩阵的秩=增广矩阵的秩=行列式的行数就有唯一解(3)当系数矩阵的秩=增广矩阵的秩<行列式的行数就有无穷解

 

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <string>
using namespace std;
const int mod=7;
int n,m;
string TIME[8]={"","MON","TUE","WED","THU","FRI","SAT","SUN"};
int matrix[310][310],ans[310];
int caldate(string s)
{
    for(int i=1;i<=7;i++)
        if(s==TIME[i])
          return i;
}
int lcm(int A,int B) //计算A与B的最小公倍数
{
    int temp=A*B;
    int r;
    while(B!=0)
    {
        r=A%B;
        A=B;
        B=r;
    }
    return temp/A;
}
void Guass()
{
    int row,col,i,j;
    for(row=0,col=0;row<m&&col<n;row++,col++) //化简增广矩阵成上三角矩阵
    {
        for(i=row;i<m;i++)//每次我们都往下挪一行并且以既定列处的元素开始化简
            if(matrix[i][col]!=0)
               break;
        if(i==m) //如果所遍历列的元素都为0则进入下一列,行数不能变
        {
            row--;
            continue;
        }
        if(i!=row) //交换两行,保证为上三角矩阵
            for(j=0;j<=n;j++)
              swap(matrix[row][j],matrix[i][j]);
        for(i=row+1;i<m;i++)
            if(matrix[i][col]!=0)
            {
                 int LCM=lcm(matrix[row][col],matrix[i][col]);
                 int x1=LCM/matrix[row][col],x2=LCM/matrix[i][col];
                 for(j=col;j<=n;j++)
                    matrix[i][j]=((matrix[i][j]*x2-matrix[row][j]*x1)%mod+mod)%mod;
            }
    }
    for(i=row;i<m;i++) //无解
        if(matrix[i][n]!=0)
        {
           cout<<"Inconsistent data."<<endl;
           return;
        }
    if(row<n) //无穷解
    {
        cout<<"Multiple solutions."<<endl;
        return ;
    }
    for (i=n-1;i>=0;i--)  //唯一解,解矩阵时应该从后往前算
    {
        int temp=matrix[i][n];
        for (j=i+1;j<n;j++)
        {
            if(matrix[i][j]!=0)
                temp=temp-(matrix[i][j]*ans[j]);
        }
        while(temp%matrix[i][i]!=0)temp+=7;
        ans[i]=temp/matrix[i][i];
        if(ans[i]<3) while(ans[i]<3) ans[i]+=7;
        else if(ans[i]>9) while(ans[i]>9) ans[i]-=7;
    }
    for(i=0;i<n;i++)
    {
        if(i==n-1)
            cout<<ans[i]<<endl;
        else
            cout<<ans[i]<<" ";
    }
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0&&m==0) //据题意说当n=0且m=0时结束
            break;
        memset(matrix,0,sizeof(matrix)); //用来存增广矩阵
        memset(ans,0,sizeof(ans)); //用来存解
        for(int i=0;i<m;i++) //一共m份档案
        {
            int k,day,x;     //k是每个人干的总件数,day是开始与结束的时间间隔(模7之后),x是部件编号
            string st,en;    //st是开始干的日子,en是干完的日子
            cin>>k>>st>>en;
            day=((caldate(en)-caldate(st)+1)%mod+mod)%mod;
            for(int j=0;j<k;j++)
            {
                scanf("%d",&x);
                matrix[i][x-1]=(matrix[i][x-1]+1)%mod;  //矩阵的前n-1列存储
            }
           matrix[i][n]=day; //增广矩阵的最后一列(列数为n)的存储
        }
        Guass();  //高斯定理
    }
    return 0;
}

你可能感兴趣的:(POJ 2947 Widget Factory (高斯消元))