好题啊,这个题告诉我们电路分析还是有用的!!
利用基尔霍夫定律,写出节点电压法的方程,然后用高斯消元解方程。节点电压法点这里。
由于两个待查询结点之间不一定是通路,所以可以利用并查集先判断一下两个点是否在一个联通分量里,如果不在,就直接输出1/0
关于列方程的一些说明,代码中会有注释。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#define LL long long
using namespace std;
int m, n, f[100];
class Frac{
public:
LL zi;
LL mu;
Frac(LL x = 0, LL y = 1)
: zi(x), mu(y) {}
Frac& operator *= (const Frac&);
Frac& operator += (const Frac&);
Frac& operator /= (const Frac&);
Frac& operator -= (const Frac&);
void print(){
printf("%lld/%lld", abs(zi), abs(mu));
}
};
Frac A[100][100];
Frac G[100][100];
LL gcd (LL a, LL b) {
return b == 0 ? (a > 0 ? a : -a) : gcd(b, a % b);
}
Frac operator + (const Frac& lhs, const Frac& rhs){
LL zi = lhs.zi*rhs.mu + lhs.mu*rhs.zi;
LL mu = lhs.mu*rhs.mu;
LL Gcd = gcd(zi, mu);
return Frac(zi/Gcd, mu/Gcd);
}
Frac operator - (const Frac& lhs, const Frac& rhs){
return lhs + Frac(-rhs.zi, rhs.mu);
}
Frac operator * (const Frac& lhs, const Frac& rhs){
LL zi = lhs.zi * rhs.zi;
LL mu = lhs.mu * rhs.mu;
LL Gcd = gcd(zi, mu);
return Frac(zi/Gcd, mu/Gcd);
}
Frac operator / (const Frac& lhs, const Frac& rhs){
return lhs * Frac(rhs.mu, rhs.zi);
}
Frac& Frac::operator += (const Frac& rhs){
(*this) = (*this) + rhs;
return (*this);
}
Frac& Frac::operator -= (const Frac& rhs){
(*this) = (*this) - rhs;
return (*this);
}
Frac& Frac::operator *= (const Frac& rhs){
(*this) = (*this) * rhs;
return (*this);
}
Frac& Frac::operator /= (const Frac& rhs){
(*this) = (*this) / rhs;
return (*this);
}
int find(int x){
return x == f[x] ? x : f[x] = find(f[x]);
}
void init(){
scanf("%d%d", &n, &m);
for(int i = 0; i < 100; i++)
for(int j = 0; j < 100; j++)
G[i][j] = Frac(0, 1);
for(int i = 0; i < 100; i++)
f[i] = i;
while(m--){
int u, v;
LL r;
scanf("%d%d%lld", &u, &v, &r);
int fu = find(u);
int fv = find(v);
f[fv] = fu;
Frac S(1, r);
G[u][v] += S;
G[v][u] += S;
}
}
Frac Gauss(int u, int v, int num){
for(int i = 0; i < num; i++)
{
int r;
for(int j = i; j < num; j++){
if(A[j][i].zi){
r = j;
break;
}
}
if(r != i)
for(int k = 0; k < num; k++)
swap(A[i][k], A[r][k]);
if(A[i][i].zi == 0)
continue;
for(int j = i+1; j < num; j++){
Frac t = A[j][i] / A[i][i];
for(int k = 0; k <= num; k++)
A[j][k] -= A[i][k]*t;
}
}
for(int i = num-1; i >= 0; i--){
for(int j = i + 1; j < num; j++){
if(A[j][j].zi)
A[i][num] -= A[i][j] * A[j][num] / A[j][j];
}
}
return A[u][num] / A[u][u] - A[v][num] / A[v][v];
}
void solve(){
int q;
int hash[100];
int num;
scanf("%d", &q);
while(q--)
{
int u, v;
scanf("%d%d", &u, &v);
printf("Resistance between %d and %d is ", u, v);
int up = find(u);
int vp = find(v);
int uk, vk;
num = 0;
for(int i = 0; i < 100; i++)
for(int j = 0; j < 100; j++)
A[i][j] = Frac(0, 1);
if(up != vp) printf("1/0\n");
else
{
for(int i = 0; i < n; i++)
{
if(i == u)
uk = num;
if(i == v)
vk = num;
int ip = find(i);
if(ip == up || ip == vp)
hash[num++] = i;
}
num++;
for(int i = 0; i < num-1; i++)
for(int j = 0; j < num-1; j++)
{
if(i == j) continue;
int x = hash[i];
int y = hash[j];
A[i][i] += G[x][y];
//加自电导
A[i][j] -= G[x][y];
//加互电导
}
//在电路里加一个1A的电流源
A[uk][num] = Frac(1, 1);
//电流源的电流入
A[vk][num] = Frac(-1, 1);
//电流源的电流出
A[num-1][0] = Frac(1, 1);
//接地点的方程,即1*V=0
/*
for(int i = 0; i <= num; i++){
for(int j = 0; j <= num; j++){
A[i][j].print();
printf(" ");
}
printf("\n");
}
*/
Frac ans = Gauss(uk, vk, num);
ans.print();
printf("\n");
}
}
}
int main()
{
//freopen("zztest.txt","r",stdin);
//freopen("zans.txt","w",stdout);
int t;
int cas = 1;
scanf("%d", &t);
while(t--)
{
printf("Case #%d:\n", cas++);
init();
solve();
printf("\n");
}
return 0;
}