Always keep a beginner's m|

luckydrawbox

园龄:4个月粉丝:1关注:2

题解 UVA1564 【Widget Factory】

前置题目

分析

我们先建立每个 string 星期对应的 int 类型,可以写一个 day(s)day(s) 函数来实现:

int day(string s)
{
	if(s=="MON")
		return 1;
	if(s=="TUE")
		return 2;
	if(s=="WED")
		return 3;
	if(s=="THU")
		return 4;
	if(s=="FRI")
		return 5;
	if(s=="SAT")
		return 6;
	if(s=="SUN")
		return 7;
}

设第 ii 种零件的生产天数为 xix_i 天,第 jj 条记录用了 bjb_j 天,第 jj 条记录的第 ii 种零件生产了 aj,ia_{j,i} 个。

显然 bj=day(Tj)day(Sj)+1b_j=day(T_j)-day(S_j)+1aj,ia_{j,i} 可以通过边输入边统计得到:

int k,x;
string S,T;
for(int i=1;i<=m;i++)
{
	cin>>k>>S>>T;
	b[i]=day(T)-day(S)+1;
	for(int j=1;j<=k;j++)
	{
		cin>>x;
		a[i][x]++;
		a[i][x]%=7;
	}
}

然后可以得出同余方程组:

{i=1na1,ixib1(mod7)i=1na2,ixib2(mod7)i=1nam,ixibm(mod7)\begin{cases}&\sum^n_{i=1}a_{1,i}x_i\equiv b_1\pmod{7}\\&\sum^n_{i=1}a_{2,i}x_i\equiv b_2\pmod{7}\\&\dots\\&\sum^n_{i=1}a_{m,i}x_i\equiv b_m\pmod{7}\end{cases}

接下来就可以套高斯消元模板,但是,先看下咱家祖传的高斯消元消去别的方程的系数时的做法:

double z=a[j][i]/a[i][i];
for(int k=i;k<=n;k++)
	a[j][k]-=a[i][k]*z;
b[j]-=b[i]*z;

这时你就会发现题目要求的答案是整数,取模时用了 double 就spfa了,怎么办呢?我们看原来消元时的公式:

aj,k=(aj,kai,kaj,i/ai,i)mod7a_{j,k}= (a_{j,k}-a_{i,k}* a_{j,i}/a_{i,i})\bmod{7}

等式两边可以同时乘一个不为零的数,为了避免除法,我们在同乘一个 ai,ia_{i,i}

aj,kai,i=(aj,kai,iai,kaj,i)mod7a_{j,k}* a_{i,i}=(a_{j,k}* a_{i,i}-a_{i,k}* a_{j,i})\bmod{7}

整个方程也可以乘 ai,ia_{i,i},这样我们就直接把 aj,ka_{j,k} 赋值为 (aj,kai,iai,kaj,i)mod7a_{j,k}* a_{i,i}-a_{i,k}* a_{j,i})\bmod{7} 即可。

最后判断解时会出现 aj,ixibj(mod7)a_{j,i}x_i\equiv b_j\pmod{7} 的形式,用扩展欧几里得算法求正整数解即可。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=310;
int n,m,a[N][N],b[N];
//日期转换 
int day(string s)
{
	if(s=="MON")
		return 1;
	if(s=="TUE")
		return 2;
	if(s=="WED")
		return 3;
	if(s=="THU")
		return 4;
	if(s=="FRI")
		return 5;
	if(s=="SAT")
		return 6;
	if(s=="SUN")
		return 7;
}
//扩展欧几里得算法(exgcd)
int exgcd(int a,int b,int &x,int &y)
{
    if(!b)
    {
        x=1,y=0;
        return a;
    }
    int d=exgcd(b,a%b,x,y);
    int z=x;x=y;y=z-y*(a/b);
    return d;
}
//高斯消元
void Gauss()
{
	int w=0;//主元个数 
	for(int i=1;i<=n;i++)
	{
		int tmp=0;//找到一个最大的x[i]项系数不为0的方程 
		for(int j=w+1;j<=m;j++)
			if(a[j][i]&&(!tmp||a[j][i]>a[tmp][i]))
				tmp=j;
		if(!tmp)
			continue;
		swap(b[++w],b[tmp]);
		for(int j=1;j<=n;j++)
			swap(a[w][j],a[tmp][j]);
		for(int j=1;j<=m;j++)//消元 
			if(w^j&&a[j][i])
			{
				int z=a[j][i];
				for(int k=1;k<=n;k++)
					a[j][k]=(a[j][k]*a[w][i]-a[w][k]*z)%7;
				b[j]=(b[j]*a[w][i]-b[w]*z)%7;
			}
	}
	for(int i=w+1;i<=m;i++)
	{
		b[i]%=7;//出现左边为0,右边不为零的方程,无解 
		if(b[i])
		{
			cout<<"Inconsistent data."<<endl;
			return;
		}
	}
	if(w<n)
	{
		cout<<"Multiple solutions."<<endl;//主元<n有无数个解 
		return;
	}
	int x,y,d;
	for(int i=1;i<=n;i++)
	{
		d=exgcd(a[i][i],7,x,y);
		if(b[i]%d)
		{
			cout<<"Inconsistent data."<<endl;//同余方程无整数解 
			return;
		}
		else 
		{
			cout<<((x*b[i]/d-3)%7+7)%7+3;//把特解转换为3~9的正整数解 
			if(i^n)//输出格式 
				cout<<" ";
			else
				cout<<endl;
		}
	}
}
int main()
{
    while(cin>>n>>m&&n&&m)
    {
    	int k,x;
    	string S,T;
    	memset(a,0,sizeof(a));
    	for(int i=1;i<=m;i++)//填入方程组系数 
    	{
    		cin>>k>>S>>T;
    		b[i]=day(T)-day(S)+1;
    		for(int j=1;j<=k;j++)
    		{
    			cin>>x;
    			a[i][x]++;
    			a[i][x]%=7;
			}
		}
		Gauss();
	}
	return 0;
}

本文作者:luckydrawbox

本文链接:https://www.cnblogs.com/luckydrawbox/p/18526681

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   luckydrawbox  阅读(2)  评论(0编辑  收藏  举报  
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起