uva 10808 Rational Resistors(基尔霍夫定律+高斯消元+并查集+分数)

好题啊,这个题告诉我们电路分析还是有用的!!

利用基尔霍夫定律,写出节点电压法的方程,然后用高斯消元解方程。节点电压法点这里

由于两个待查询结点之间不一定是通路,所以可以利用并查集先判断一下两个点是否在一个联通分量里,如果不在,就直接输出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;
}
	

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值