poj2947——Widget Factory(高斯消元求模线性方程)

版权声明:本文为博主原创文章,点个赞随便转 https://blog.csdn.net/qq_20200047/article/details/71158706
Widget Factory
Time Limit: 7000MS   Memory Limit: 65536K
Total Submissions: 5977   Accepted: 2082

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. 

Source


解题思路:

被这个题意弄得真的无语了,读了N遍.......

公司被吞并,老员工几乎全部被炒鱿鱼。一共有n种不同的工具,编号1-N(代码中是0—N-1), 每种工具的加工时间为3—9天 ,但是现在老员工不在我们不知道每种工具的加工时间,庆幸的是还保留着一些对工人制造工具的记录,对于每个老员工,他的记录包括,他开始工作的时间(在某个星期的星期几),被炒鱿鱼的时间(某个星期的星期几),在第几个星期不知道.....在这段时间里,他正好加工了k件物品,给出了这k件物品的编号。我们要做的就是通过这些记录,来确定每种工具的加工时间是多少。

思路为建立线性方程组(涉及到了取模):

扫描二维码关注公众号,回复: 5014784 查看本文章

对于每个记录,建立一个方程,所有的记录,建立为如下的方程:

(a[0][0]*X0 + a[0][1] *X1 + a[0][2]*X2+...........a[0][n-1]*Xn-1 )  %7=  a[0][n]

(a[1][0]*X0 + a[1][1] *X1 + a[1][2]*X2+...........a[1][n-1]*Xn-1 )  %7=  a[1][n]

................................................................................................................................

(a[m-1][0]*X0 + a[m-1][1] *X1 + a[m-1][2]*X2+...........a[m-1][n-1]*Xn-1 )  %7=  a[m-1][n]

如上方程组,表示的是一共有m个记录,即有m个方程,有n个变量(表示n个物品,编号0-N-1),方程中的x0, x1, x2........xn-1,代表的是第i种工具加工需要多长时间

a[ i ] [ j ]  (0<=j<=n-1) ,表示第i个方程中(i从0开始),编号为j的物品,加工的个数,即Xj,  a[i][n] ,表示第i个方程中,加工完所有种类的工具,需要的时间,因为不知道开始时间和结束时间是在第几个星期,只知道星期几,所以有 %7.

以样例中的例子,来说一下方程组的建立.

2 3
2 MON THU
1 2
3 MON FRI
1 1 2
3 MON SUN
1 2 2

初始化,增广矩阵a[][]初始化为0.

样例中 2 3 代表 有一共两种工具(编号0,1), 3个工人的记录。

第一个工人的记录为 2 表示他加工了两个工具,开始时间是 MON,表示星期一,结束时间为THU,表示星期四,下一行的1,2表示,他加工两个工具,这两个工具的编号,1,2(代码中是 0,1)

这样建立的方程为   (1* X0+ 1 *X2 )%7= ((4-1)%7+7)%7 ,方程后边得是正整数.

第二个工人的记录为3 表示他加工了三个工具,开始时间为 MON,表示星期一,结束时间为FRI,表示星期五,下一行的1,1,2表示,他加工的三个工具里面,编号分别是1,1,2,也就是第一种工具加工了两个,第二种工具加工了1个。

这样建立的方程为  (2*X0+1*X2)%7=((5-1)%7+7)%7,

依次类推,

第三个工人的记录建立的方程为  (1*X0+2*X2) %7 =((7-1)%7+7)%7.

我们要做的就是根据这三个方程,把x0,x1,解出来。

当没有解时,输出  Inconsistent data. 无穷解时 输出 Multiple solutions. ,否则输出每种工具的加工时间...

这种题目需要把模板修改一下...一上午就研究了个模板是怎么回事....

代码:

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
using namespace std;
const int maxn = 310;
int equ, var; // 有equ个方程,var个变元。增广阵行数为equ, 分别为0到equ - 1,列数为var + 1,分别为0到var.
int a[maxn][maxn];//增广矩阵
int x[maxn]; // 解集.
int free_num;


inline int gcd(int a, int b)
{
	int t;
	while(b!=0)
	{
		t=b;
		b=a%b;
		a=t;
	}
	return a;
}

inline int lcm(int a, int b)
{
	return a*b/gcd(a,b);
}
// 高斯消元法解方程组(Gauss-Jordan elimination).(-2表示有浮点数解,但无整数解,-1表示无解,0表示唯一解,大于0表示无穷解,并返回自由变元的个数)
int change(char s[])
{
	if(strcmp(s,"MON")==0) return 1;
	else if(strcmp(s,"TUE")==0) return 2;
	else if(strcmp(s,"WED")==0) return 3;
	else if(strcmp(s,"THU")==0) return 4;
	else if(strcmp(s,"FRI")==0) return 5;
	else if(strcmp(s,"SAT")==0) return 6;
	else  return 7;
}
int Gauss(void)
{
	int i,j,k;
	int max_r; // 当前这列绝对值最大的行.
	int col; // 当前处理的列.
	int ta, tb;
	int LCM;
	int temp;
	// 转换为阶梯阵.
	col = 0; // 当前处理的列.
	for (k = 0; k < equ && col < var; k++, col++)
	{ // 枚举当前处理的行.
		// 找到该col列元素绝对值最大的那行与第k行交换.(为了在除法时减小误差)
		max_r = k;
		for (i = k + 1; i < equ; i++)
		{
			if (abs(a[i][col]) > abs(a[max_r][col])) max_r = i;
		}
		if (max_r != k)
		{ // 与第k行交换.
			for (j = k; j < var + 1; j++) swap(a[k][j], a[max_r][j]);
		}
		if (a[k][col] == 0)
		{ // 说明该col列第k行以下全是0了,则处理当前行的下一列.
			k--;
			continue;
		}
		for (i = k + 1; i < equ; i++)
		{ // 枚举要删去的行.
			if (a[i][col] != 0)
			{
				LCM = lcm(abs(a[i][col]), abs(a[k][col]));
				ta = LCM / abs(a[i][col]), tb = LCM / abs(a[k][col]);
				if (a[i][col] * a[k][col] < 0) tb = -tb; // 异号的情况是两个数相加.
				for (j = col; j < var + 1; j++)
				{
					a[i][j] =(((a[i][j] * ta - a[k][j] * tb)%7+7)%7);
				}
			}
		}
	}
	//Debug();
	// 1. 无解的情况: 化简的增广阵中存在(0, 0, ..., a)这样的行(a != 0).
	for (i = k; i < equ; i++)
	{ // 对于无穷解来说,如果要判断哪些是自由变元,那么初等行变换中的交换就会影响,则要记录交换.
		if (a[i][col] != 0) return -1;
	}
	// 2. 无穷解的情况: 在var * (var + 1)的增广阵中出现(0, 0, ..., 0)这样的行,即说明没有形成严格的上三角阵.
	// 且出现的行数即为自由变元的个数.
	if (k < var)
		return var - k; // 自由变元有var - k个.
	// 3. 唯一解的情况: 在var * (var + 1)的增广阵中形成严格的上三角阵.
	// 计算出Xn-1, Xn-2 ... X0.
	for (i = var - 1; i >= 0; i--)
	{
		temp = a[i][var];//等式右边的数
		for (j = i + 1; j < var; j++)
		{
			if (a[i][j] != 0) temp -= a[i][j] * x[j];//把已知的解带入,减去,只剩下,一个未知的解
			temp=(temp%7+7)%7;
		}
		while(temp%a[i][i]!=0)//外层每次循环都是为了求 a[i][i],因为它是每个方程中唯一一个未知的变量(求该方程时)
			temp+=7;//因为天数不确定,而a[i][i]必须得为整数才可以,周期为7
		x[i]=(temp/a[i][i])%7;
	}
	return 0;
}

int main(void)
{
	int n,m,k,num;
	char s[5],e[5];
	while(scanf("%d%d",&n,&m)!=EOF&&(n||m))
	{
		memset(a,0,sizeof(a));
		for(int i=0;i<m;i++)
		{
			scanf("%d",&k);
			scanf("%s%s",s,e);
			a[i][n]=((change(e)-change(s)+1)%7+7)%7;
			for(int j=1;j<=k;j++)//k是他打造的数量
			{
				scanf("%d",&num);//可能是相同的数
				num--;
				a[i][num]++;//系数++
				a[i][num]%=7;//有重复的。
			}
		}
		equ=m;//有m个方程
		var=n;//有多少个变量
		free_num = Gauss();
		if(free_num==0)
		{
			for(int i=0;i<n;i++)//根据题意,每个零件的加工时间在3-9天.
				if(x[i]<=2)
				x[i]+=7;
			for(int i=0;i<n-1;i++)
			 cout<<x[i]<<" ";
			cout<<x[n-1]<<endl;
		}
		else if(free_num==-1)
			cout<<"Inconsistent data."<<endl;
		else
			cout<<"Multiple solutions."<<endl;
	}
	return 0;
}
整数型高斯消元解方程组模板 http://blog.csdn.net/duanxian0621/article/details/7408887

/* 用于求整数解得方程组. */

#include <iostream>
#include <string>
#include <cmath>
using namespace std;

const int maxn = 105;

int equ, var; // 有equ个方程,var个变元。增广阵行数为equ, 分别为0到equ - 1,列数为var + 1,分别为0到var.
int a[maxn][maxn];
int x[maxn]; // 解集.
bool free_x[maxn]; // 判断是否是不确定的变元.
int free_num;

void Debug(void)
{
    int i, j;
    for (i = 0; i < equ; i++)
    {
        for (j = 0; j < var + 1; j++)
        {
            cout << a[i][j] << " ";
        }
        cout << endl;
    }
    cout << endl;
}

inline int gcd(int a, int b)
{
    int t;
    while (b != 0)
    {
        t = b;
        b = a % b;
        a = t;
    }
    return a;
}

inline int lcm(int a, int b)
{
    return a * b / gcd(a, b);
}

// 高斯消元法解方程组(Gauss-Jordan elimination).(-2表示有浮点数解,但无整数解,-1表示无解,0表示唯一解,大于0表示无穷解,并返回自由变元的个数)
int Gauss(void)
{
    int i, j, k;
    int max_r; // 当前这列绝对值最大的行.
int col; // 当前处理的列.
    int ta, tb;
    int LCM;
    int temp;
    int free_x_num;
    int free_index;
    // 转换为阶梯阵.
    col = 0; // 当前处理的列.
    for (k = 0; k < equ && col < var; k++, col++)
    { // 枚举当前处理的行.
        // 找到该col列元素绝对值最大的那行与第k行交换.(为了在除法时减小误差)
        max_r = k;
        for (i = k + 1; i < equ; i++)
        {
            if (abs(a[i][col]) > abs(a[max_r][col])) max_r = i;
        }
        if (max_r != k)
        { // 与第k行交换.
            for (j = k; j < var + 1; j++) swap(a[k][j], a[max_r][j]);
        }
        if (a[k][col] == 0)
        { // 说明该col列第k行以下全是0了,则处理当前行的下一列.
            k--; continue;
        }
        for (i = k + 1; i < equ; i++)
        { // 枚举要删去的行.
            if (a[i][col] != 0)
    {
                LCM = lcm(abs(a[i][col]), abs(a[k][col]));
                ta = LCM / abs(a[i][col]), tb = LCM / abs(a[k][col]);
                if (a[i][col] * a[k][col] < 0) tb = -tb; // 异号的情况是两个数相加.
                for (j = col; j < var + 1; j++)
                {
                    a[i][j] = a[i][j] * ta - a[k][j] * tb;
                }
    }
        }
    }
    Debug();
    // 1. 无解的情况: 化简的增广阵中存在(0, 0, ..., a)这样的行(a != 0).
    for (i = k; i < equ; i++)
    { // 对于无穷解来说,如果要判断哪些是自由变元,那么初等行变换中的交换就会影响,则要记录交换.
        if (a[i][col] != 0) return -1;
    }
    // 2. 无穷解的情况: 在var * (var + 1)的增广阵中出现(0, 0, ..., 0)这样的行,即说明没有形成严格的上三角阵.
    // 且出现的行数即为自由变元的个数.
    if (k < var)
    {
        // 首先,自由变元有var - k个,即不确定的变元至少有var - k个.
        for (i = k - 1; i >= 0; i--)
        {
            // 第i行一定不会是(0, 0, ..., 0)的情况,因为这样的行是在第k行到第equ行.
            // 同样,第i行一定不会是(0, 0, ..., a), a != 0的情况,这样的无解的.
            free_x_num = 0; // 用于判断该行中的不确定的变元的个数,如果超过1个,则无法求解,它们仍然为不确定的变元.
            for (j = 0; j < var; j++)
            {
                if (a[i][j] != 0 && free_x[j]) free_x_num++, free_index = j;
            }
            if (free_x_num > 1) continue; // 无法求解出确定的变元.
            // 说明就只有一个不确定的变元free_index,那么可以求解出该变元,且该变元是确定的.
            temp = a[i][var];
            for (j = 0; j < var; j++)
            {
                if (a[i][j] != 0 && j != free_index) temp -= a[i][j] * x[j];
            }
            x[free_index] = temp / a[i][free_index]; // 求出该变元.
            free_x[free_index] = 0; // 该变元是确定的.
        }
        return var - k; // 自由变元有var - k个.
    }
    // 3. 唯一解的情况: 在var * (var + 1)的增广阵中形成严格的上三角阵.
    // 计算出Xn-1, Xn-2 ... X0.
    for (i = var - 1; i >= 0; i--)
    {
        temp = a[i][var];
        for (j = i + 1; j < var; j++)
        {
            if (a[i][j] != 0) temp -= a[i][j] * x[j];
        }
        if (temp % a[i][i] != 0) return -2; // 说明有浮点数解,但无整数解.
        x[i] = temp / a[i][i];
    }
return 0;
}

int main(void)
{
    freopen("Input.txt", "r", stdin);
    int i, j;
    while (scanf("%d %d", &equ, &var) != EOF)
    {
        memset(a, 0, sizeof(a));
   memset(x, 0, sizeof(x));
   memset(free_x, 1, sizeof(free_x)); // 一开始全是不确定的变元.
        for (i = 0; i < equ; i++)
        {
            for (j = 0; j < var + 1; j++)
            {
                scanf("%d", &a[i][j]);
            }
        }
//        Debug();
        free_num = Gauss();
        if (free_num == -1) printf("无解!\n");
   else if (free_num == -2) printf("有浮点数解,无整数解!\n");
        else if (free_num > 0)
        {
            printf("无穷多解! 自由变元个数为%d\n", free_num);
            for (i = 0; i < var; i++)
            {
                if (free_x[i]) printf("x%d 是不确定的\n", i + 1);
                else printf("x%d: %d\n", i + 1, x[i]);
            }
        }
        else
        {
            for (i = 0; i < var; i++)
            {
                printf("x%d: %d\n", i + 1, x[i]);
            }
        }
        printf("\n");
    }
    return 0;
}

这里还有一个感觉不错,先存一下

题目是要求建立一个方程组:

(mat[1][1]*x[1] + mat[1][2]*x[2] + … + mat[1][n]*x[n])%7 =mat[1][n+1]

(mat[2][1]*x[1] + mat[2][2]*x[2] + … + mat[2][n]*x[n])%7 =mat[2][n+1]

(mat[m][1]*x[1] + mat[m][2]*x[2] + … + mat[m][n]*x[n])%7 =mat[m][n+1]

如果有解输出解得个数,如果无解Inconsistent data.无穷多组解Multiple solutions.


扯一句,什么时候无解?

系数矩阵的秩 不等于 增广矩阵的秩 时。反映在程序上就是:

for (i = row; i < N; i++)
{
if (a[i][M] != 0)
{
printf("Inconsistent data.\n");
}
}

什么时候无穷多解?

当增广矩阵的秩小于行列式的行数的时候。 反映在程序上就是:

if (row < M)
{
printf("Multiple solutions.\n");
}

好了,程序如下:


#include<iostream>
#include<cstdio>
#include<string.h>

using namespace std;

#define MOD 7

int a[310][310];
int N, M;
char str1[5], str2[5];
int ans[310];

int solved(char s [])
{
    if (s[0] == 'M'return 1;
    if (s[0] == 'T' && s[1] == 'U'return 2;
    if (s[0] == 'W'return 3;
    if (s[0] == 'T' && s[1] == 'H'return 4;
    if (s[0] == 'F'return 5;
    if (s[0] == 'S' && s[1] == 'A'return 6;
    if (s[0] == 'S' && s[1] == 'U'return 7;
}

int extend_gcd(int A, int B, int &x, int &y)
{
    if (B == 0)
    {
        x = 1, y = 0;
        return A;
    }
    else
    {
        int r = extend_gcd(B, A%B, x, y);
        int t = x;
        x = y;
        y = t - A / B*y;
        return r;
    }
}

int lcm(int A, int B)
{
    int x = 0, y = 0;
    return A*B / extend_gcd(A, B, x, y);
}

void Guass()
{
    int i, j, row, col;
    for (row = 0, col = 0; row < N && col < M; row++, col++)
    {
        for (i = row; i < N; i++)
            if (a[i][col]) break;
        if (i == N)
        {
            row--;
            continue;
        }
        if (i != row)
            for (j = 0; j <= M; j++) swap(a[row][j], a[i][j]);
        for (i = row + 1; i < N; i++)
        {
            if (a[i][col])
            {
                int LCM = lcm(a[row][col], a[i][col]);//利用最小公倍数去化上三角
                int ch1 = LCM / a[row][col], ch2 = LCM / a[i][col];
                for (j = col; j <= M; j++)
                    a[i][j] = ((a[i][j] * ch2 - a[row][j] * ch1)%MOD + MOD)%MOD;
            }

        }

    }

    for (i = row; i < N; i++)//无解
    {
        if (a[i][M] != 0)
        {
            printf("Inconsistent data.\n");
            return;
        }
    }
    if (row < M)//无穷多解
    {
        printf("Multiple solutions.\n");
        return;
    }
    //唯一解时
    for (i = M - 1; i >= 0; i--)
    {
        int ch = 0;
        for (j = i + 1; j < M; j++)
        {
            ch = (ch + ans[j] * a[i][j] % MOD)%MOD;
        }
        int last = ((a[i][M] - ch)%MOD + MOD)%MOD;
        int x = 0, y = 0;
        int d = extend_gcd(a[i][i], MOD, x, y);
        x %= MOD;
        if (x < 0) x += MOD;
        ans[i] = last*x / d%MOD;
        if (ans[i] < 3) ans[i] += 7;
    }
    for (int i = 0; i < M; i++)
    {
        if (i == 0)
            printf("%d", ans[i]);
        else
            printf(" %d", ans[i]);

    }
    printf("\n");
}

int main()
{

    while (scanf("%d%d", &M, &N), N || M)
    {
        memset(a, 0sizeof(a));
        memset(ans, 0sizeof(ans));
        int K;

        for (int i = 0; i < N; i++)
        {
            scanf("%d%s%s", &K, str1, str2);
            int last = ((solved(str2) - solved(str1) + 1)%MOD + MOD)%MOD;

            for (int j = 0; j < K; j++)
            {
                int v;
                scanf("%d", &v);
                a[i][v - 1] = (a[i][v - 1] + 1)%MOD;
            }
            a[i][M] = last;
        }
        Guass();
    }
}

猜你喜欢

转载自blog.csdn.net/qq_20200047/article/details/71158706
今日推荐