【BZOJ2419】电阻(物理题)

点此看题面

大致题意: 一个电路板上有(n)个接点和(m)个电阻,给出每个电阻阻值及其在哪两个接点之间,求(1)(n)两点间的等效电阻。

基尔霍夫方程组

基尔霍夫方程组应该是一个众所周知的东西,可以分为节点定律和回路定律。

一开始很(naive)地想去假设每个电阻的电流,结果发现似乎只能对于每个节点列出方程,而电阻数很可能超过节点数,无法得出解。

于是,只能去假设第(i)个电阻的电势为(varphi_i),那么流经一个电阻(R_{x,y})的电流可以看作(frac{varphi_x-varphi_y}{R_{x,y}})(此处暂且不考虑正负性)。

对于除(1)(n)以外的每一个节点,有电流之和为零((1)号点电流和为(I_{total})),也就是:

[sumfrac{varphi_i-varphi_j}{R_{i,j}}=0 ]

然后我们不妨设(varphi_n=0,I_{total=1}),则:对于(1)号点,直接修改右式的(0)(1)即可;对于(n)号点,已知(varphi_n=0),那么不用再列与它有关的方程,且可以直接从所有方程中将(varphi_n)这一项删去。

接下来只要高斯消元,并得到最终答案(R_{total}=frac{varphi_1-varphi_n}{I_{total}}=phi_1)

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100
#define DB double
#define eps 1e-8
using namespace std;
int n,m;
namespace Gauss//高斯消元
{
	int S;DB a[N+5][N+5],v[N+5];I void swap(DB& x,DB& y) {DB t=x;x=y,y=t;}
	I void Clear() {memset(a,0,sizeof(a)),memset(v,0,sizeof(v));}
	I void Find(CI x)
	{
		if(fabs(a[x][x])>eps) return;RI i,j;for(i=x+1;fabs(a[i][x])<eps;++i);
		for(j=x;j<=S;++j) swap(a[x][j],a[i][j]);
	}
	I void Calc()
	{
		RI i,j,k;DB t;for(i=1;i<=S;++i) for(Find(i),j=i+1;j<=S;++j)
			for(v[j]+=(t=-a[j][i]/a[i][i])*v[i],k=i;k<=n;++k) a[j][k]+=t*a[i][k];
		for(i=S;i;--i) for(v[i]/=a[i][i],j=i-1;j;--j) v[j]-=a[j][i]*v[i];
	}
}using namespace Gauss;
int main()
{
	RI i,x,y,R;W(scanf("%d%d",&n,&m)==2)
	{
		for(Clear(),i=1;i<=m;++i) scanf("%d%d%d",&x,&y,&R),
			a[x][x]+=1.0/R,a[y][y]+=1.0/R,a[x][y]-=1.0/R,a[y][x]-=1.0/R;//确定方程系数
		v[1]=1,S=n-1,Calc(),printf("%.2lf
",v[1]);//1号点右式为1,令S=n-1忽略第n项
	}return 0;
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2419.html