从Codeforces GYM 100644H: We’ve Got Chemistry, Babe 中学习高斯消元

本文介绍了一种使用高斯消元法解决化学方程配平问题的方法,并提供了一个C++实现的例子。该方法通过设定化学计量数为未知数,构建并求解线性方程组来实现自动配平。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这道题是裸的高斯消元,设每种物质前面的化学计量数是xi,写方程出来解就可以了

有几个注意点:

1.化学计量数的方程一定是一个不定方程组,为了保证方程组有唯一解,加一个x1=1

2.化学计量数一定要是整数,所以要用分数来表示方程的解,最后通分

   为了防止通分时最小公分母过大,最好开long long

3.可以用递归风骚的处理字符串


贴代码

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <utility>
#include <map>
#include <stack>
#include <set>
#include <vector>
#include <queue>
#include <deque>
#include <sstream>
#define x first
#define y second
#define mp make_pair
#define pb push_back
#define LL long long
#define LOWBIT(x) x & (-x)
using namespace std;

const int MOD=1e9+7;
const int INF=0x7ffffff;
const int magic=348;
typedef pair<LL,LL> Pair;

int ll,rr;
int n=0,m;
LL b[48][48];
map<string,LL> ID;
map<LL,LL>::iterator iter;
Pair sol[48];
LL res[48];

inline LL gcd(LL x,LL y)
{
	if (y==0) return x; else return gcd(y,x%y);
}

inline LL lcm(LL x,LL y)
{
	LL res=gcd(x,y);
	return x/res*y;
}

inline LL myabs(LL x)
{
	return x>=0?x:-x;
}

inline void clear(int x)
{
	for (int i=1;i<=m;i++) b[x][i]=0;
}

inline LL getID(string el)
{
	if (ID[el]) return ID[el];
	ID[el]=++n;clear(n);
	return n;
}

LL readcoef(string &s)
{
	if (!isdigit(s[0])) return 1;
	int len=1;LL res;
	while (len<=s.size() && isdigit(s[len])) len++;
	stringstream ss(s.substr(0,len));
	s.erase(0,len);
	ss>>res;
	return res;
}

void doit(string &s,map<LL,LL> &M)
{
	if (s.size()==0) return;
	if (isupper(s[0]))
	{
		int len=1;
		while (islower(s[len]) && len<=s.size()-1) len++;
		string el=s.substr(0,len);s.erase(0,len);
		LL num=getID(el);
		M[num]+=readcoef(s);
		doit(s,M);
	}
	if (s[0]=='(')
	{
		s.erase(0,1);
		map<LL,LL> tmp;doit(s,tmp);
		s.erase(0,1);
		LL coef=readcoef(s);
		for (iter=tmp.begin();iter!=tmp.end();iter++) M[iter->first]+=iter->second*coef;
		doit(s,M);
	}
}

inline void swap(int r1,int r2)
{
	for (int i=1;i<=m;i++) swap(b[r1][i],b[r2][i]);
}

inline void remove(int r1,int r2,int col)
{
	if (b[r2][col]==0) return;
	LL lc=lcm(myabs(b[r1][col]),myabs(b[r2][col]));
	LL lr1=lc/b[r1][col],lr2=lc/b[r2][col];
	for (int i=col;i<=m;i++) b[r2][i]=lr2*b[r2][i]-lr1*b[r1][i];
}

Pair frac(LL x,LL y)
{
	LL g=gcd(myabs(x),myabs(y));
	x/=g;y/=g;
	if (y<0)
	{
		x=-x;
		y=-y;
	}
	return mp(x,y);
}

bool solve()
{
	int i,j,rr;
	for (j=1;j<=m-1;j++)
	{
		rr=j;
		while (rr<=n && b[rr][j]==0) rr++;
		if (rr==n+1) return false;
		if (rr!=j) swap(j,rr);
		for (i=rr+1;i<=n;i++) remove(j,i,j);
	}
	for (i=m;i<=n;i++) if (b[i][m]!=0) return false;
	sol[m-1]=frac(b[m-1][m],b[m-1][m-1]);
	if (sol[m-1].x<=0) return false;
	for (i=m-2;i>=1;i--)
	{
		LL lc=1,val;
		for (j=i+1;j<=m-1;j++)
			lc=lcm(lc,sol[j].y/gcd(sol[j].y,myabs(b[i][j])));
		for (j=i;j<=m;j++) b[i][j]*=lc;
		val=b[i][m];
		for (j=m-1;j>=i+1;j--) val-=b[i][j]/sol[j].y*sol[j].x;
		sol[i]=frac(val,b[i][i]);
		if (sol[i].x<=0) return false;
		
	}
	LL lc=1;
	for (i=1;i<=m-1;i++) lc=lcm(lc,sol[i].y);
	for (i=1;i<=m-1;i++) res[i]=sol[i].x*lc/sol[i].y;
	return true;
}

int main ()
{
	int i,j,mul,tot=0;
	string s;
	while (scanf("%d%d",&ll,&rr)==2 && ll && rr)
	{
		m=ll+rr+1;
		memset(b,0,sizeof(b));
		n=0;ID.clear();
		for (i=1;i<=m-1;i++)
		{
			cin>>s;
			map<LL,LL> M;
			doit(s,M);
			mul=i<=ll?1:-1;
			for (j=1;j<=n;j++) b[j][i]=mul*M[j];
		}
		clear(++n);b[n][1]=1;b[n][m]=1;
		printf("Case %d: ",++tot);
		if (!solve())
		{
			printf("No\n");
			continue;
		}
		for (i=1;i<=ll+rr;i++) printf("%d ",res[i]);
		printf("\n");
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值