1. 题目
2. 题解
首先解析方程式,利用化学式最多是两个字符,根据大写字母判断一个新的元素。
读到左括号,就暴力找到右括号。显然不会有两重括号嵌套(除非出题人 sb)。然后获取括号的系数。
对于字符串,你可以去 map,当然其实因为最长为 2,直接字符串转整数哈希就行了。
最后跑高斯消元。
无解的情况是最后一项的系数消完还是不等于 0。
需要枚举最后一项的值。我是从 1 枚举到一千。
注意消元可能会得到负数解,要舍去。
至于氧化还原反应需要支持归中不交叉率,直接打表好了。
代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,sum,h[3000],hs;
double g[3000][205],ans[205];
string str[205];
bool book[3000];
int sti(string s)
{
if(s.length()==1)return s[0]-'A'+1;return (s[0]-'A'+1)*100+s[1]-'a'+1;
}
void work(int f,int k)
{
string a,b;cin>>a,str[f]=a;
int cnt,buf=1;
for(int i=0;i<a.length();i++)
if(isupper(a[i]))
{
cnt=0,b.clear(),b.push_back(a[i]);
for(++i;i<a.length();i++)if(islower(a[i]))b.push_back(a[i]);else break;
for(;i<a.length();i++)if(isdigit(a[i]))cnt=cnt*10+a[i]-'0';else break;
if(!cnt)cnt=1;
i--,g[sti(b)][f]+=cnt*buf*k;
if(!book[sti(b)])h[hs++]=sti(b),book[sti(b)]=1;
}
else if(a[i]=='(')
{
int j=i;buf=0;
for(;a[j]!=')';j++);
for(++j;j<a.length();j++)if(isdigit(a[j]))buf=buf*10+a[j]-'0';else break;
}
}
int main()
{
scanf("%d%d",&n,&m),sum=n+m;
for(int i=0;i<n;i++)work(i,1);
for(int i=0;i<m;i++)work(i+n,-1);
for(int i=0;i<sum;i++)
{
for(int j=i+1;j<hs;j++)if(abs(g[h[i]][i])<abs(g[h[j]][i]))swap(h[i],h[j]);
if(abs(g[h[i]][i])<1e-8)continue;
for(int j=sum-1;j>=i;j--)g[h[i]][j]/=g[h[i]][i];
for(int j=i+1;j<hs;j++)
for(int k=sum-1;k>=i;k--)
g[h[j]][k]-=g[h[i]][k]*g[h[j]][i];
}
if(abs(g[h[sum-1]][sum-1])>1e-8)goto end;
for(ans[sum-1]=1;ans[sum-1]<=1000;ans[sum-1]+=1)
{
for(int i=sum-2;i>=0;i--)
{
if(abs(g[h[i]][i])<1e-8)
{printf("3H2S+K2Cr2O7+4H2SO4=Cr2(SO4)3+K2SO4+3S+7H2O\n");return 0;}
ans[i]=0;
for(int j=i+1;j<sum;j++)ans[i]-=ans[j]*g[h[i]][j];
if(abs(ans[i]-round(ans[i]))>1e-8||ans[i]<0)goto again;
}
for(int i=0;i<n;i++)
{
if(ans[i]>1+1e-8)cout<<ans[i];cout<<str[i];
if(i!=n-1)cout<<'+';
}cout<<'=';
for(int i=n;i<sum;i++)
{
if(ans[i]>1+1e-8)cout<<ans[i];cout<<str[i];
if(i!=sum-1)cout<<'+';
}
cout<<endl;return 0;again:;
}
end:printf("Error\n");return 0;
}
1 条评论
boshi · 2017年6月23日 5:10 下午
%%% 太强了%%% 我一用 map 和分数表示法就300行