跳到主要内容
原文530 词数3 分钟

题解:SP2415 RESIST - Kirchhof Law

概述

给定 n 点 m 边的无向图,边权为电阻阻值,求节点 1 到 n 的等效电阻。设各节点电势,由欧姆定律得每条边的电流,再对每个普通节点用基尔霍夫电流定律令支路电流代数和为 0,每条边给系数矩阵对应的对角与非对角元贡献电导,组成关于各点电势的线性方程组。在节点 1 注入 1 安培电流、把节点 n 设为零电势,高斯消元解出节点 1 的电势即为等效电阻。时间复杂度 O(n 的 3 次方)。

参考资料​

题意简述​

给定一张 nn 个节点 mm 条边的无向图,边权表示电阻阻值,求节点 11 到 nn 的等效电阻。

解题思路​

设节点 xx 的电势为 φx\varphi_x,边 (u,v)(u,v) 的电阻为 Ru,vR_{u,v},则电导为:

Gu,v=1Ru,vG_{u,v}=\frac{1}{R_{u,v}}

根据 欧姆定律,从 uu 到 vv 的电流为:

Iu,v=φu−φvRu,v=Gu,v(φu−φv)I_{u,v}=\frac{\varphi_u-\varphi_v}{R_{u,v}}=G_{u,v}(\varphi_u-\varphi_v)

根据 基尔霍夫电流定律,对于任意一个普通节点,所有关联支路电流的代数和为 00。

考虑节点 uu 的电流:

∑(u,v)∈EIu,v=∑(u,v)∈EGu,v(φu−φv)=0\sum_{(u,v)\in E}I_{u,v}=\sum_{(u,v)\in E}G_{u,v}(\varphi_u-\varphi_v)=0

展开可得:

(∑(u,v)∈EGu,v)φu−∑(u,v)∈EGu,vφv=0\left(\sum_{(u,v)\in E}G_{u,v}\right)\varphi_u-\sum_{(u,v)\in E}G_{u,v}\varphi_v=0

这是一个关于各点电势的线性方程。

au,1φ1+au,2φ2+⋯+au,nφn=bua_{u,1}\varphi_1+a_{u,2}\varphi_2+\dots+a_{u,n}\varphi_n=b_u

考虑添加一条边 (u,v)(u,v),设其电导为 GG。节点 uu 的方程加入 G(φu−φv)G(\varphi_u-\varphi_v),节点 vv 的方程加入 G(φv−φu)G(\varphi_v-\varphi_u),因此对应到系数矩阵就是:

au,u←au,u+Ga_{u,u}\gets a_{u,u}+G au,v←au,v−Ga_{u,v}\gets a_{u,v}-G av,v←av,v+Ga_{v,v}\gets a_{v,v}+G av,u←av,u−Ga_{v,u}\gets a_{v,u}-G

把所有边的贡献都加到矩阵里,就得到了整个线性方程组。

我们可以在节点 11 通入 1A1\mathrm{A} 电流,并将节点 nn 设为零电势点,求出节点 11 的电势。

b1=1,φn=0b_1=1,\varphi_n=0

利用 高斯消元 求解各点电势后,等效电阻为:

R=UI=φ1−φn1=φ1R=\frac{U}{I}=\frac{\varphi_1-\varphi_n}{1}=\varphi_1

参考代码​

857 Bcpp
#include <bits/stdc++.h>
using namespace std;

const double eps=1e-8;
const int N=105;
double a[N][N];
bool gauss(int n)
{
for(int i=1;i<=n;i++)
{
int t=i;
for(int j=i+1;j<=n;j++)
{
if(fabs(a[j][i])>fabs(a[t][i]))t=j;
}
if(fabs(a[t][i])<eps)return 0;
swap(a[i],a[t]);
for(int j=n+1;j>=i;j--)a[i][j]/=a[i][i];
for(int j=1;j<=n;j++)
{
if(i==j)continue;
for(int k=n+1;k>i;k--)a[j][k]-=a[i][k]*a[j][i];
}
}
return 1;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,m;
while(cin>>n>>m)
{
while(m--)
{
int u,v;
double r;
cin>>u>>v>>r;
double g=1/r;
a[u][u]+=g;
a[u][v]-=g;
a[v][v]+=g;
a[v][u]-=g;
}
a[1][n+1]=1;
for(int i=1;i<n;i++)a[n][i]=0;
a[n][n]=1;
a[n][n+1]=0;
gauss(n);
cout<<fixed<<setprecision(2)<<a[1][n+1]<<'\n';
memset(a,0,sizeof a);
}
return 0;
}