【hdu5006】基尔霍夫+高斯消元

4 篇文章 0 订阅
给出n个点m条边的无向图
每条边有权值,为0或1,表示两点间电阻
可能有自环与重边
给定S,T
求从S到T的电阻
1 ≤ N ≤ 10000, M = 4N
无向图的边与边权均为等概率随机生成的

首先合并多余的点,发现边权随机生成,合并后点数不多。基尔霍夫定律,每个点电流流入等于电流流出,设S到T电压为1,每个点的电势为xi,列出方程。然后求出总电流,即可得到电阻。

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdio>
using namespace std;
typedef long long LL;
const int N = 10005, D = 1005;
struct arr { int x, y, z; } q[N*4];
double X[N], a[D][D], ans, p[D], Q;
bool v[N];
int n, m, S, T, ts, par[N], b[N], map[D][D];
int find(int x) { return par[x] == x ? x : par[x] = find(par[x]); }
int main()
{
    cin >> Q;
    while(Q --) {
        scanf ("%d%d%d%d", &n, &m, &S, &T);
        ts = 0;
        memset (map, 0, sizeof(map));
        memset (a, 0, sizeof(a));
        memset (p, 0, sizeof(p));
        memset (v, 0, sizeof(v));
        memset (b, 0, sizeof(b));
        memset (X, 0, sizeof(X));
        ans = 0;
        for (int i = 1; i <= n; i ++) par[i] = i;
        for (int i = 1; i <= m; i ++) {
            scanf ("%d%d%d", &q[i].x, &q[i].y, &q[i].z);
        }
        for (int i = 1; i <= m; i ++) {
            if (q[i].z == 1) continue ;
            int x = find(q[i].x), y = find(q[i].y);
            if (x != y) par[x] = y;
        }
        if (find(S) == find(T)) { puts("0.000000"); continue ; }
        for (int i = 1; i <= n; i ++) {
            int x = find(i);
            if (!v[x]) v[x] = 1, b[x] = ++ ts;
            b[i] = b[x];
        }
        for (int i = 1; i <= m; i ++) {
            if (q[i].z == 0) continue ;
            int x = b[q[i].x], y = b[q[i].y];
            if (x == y) continue ;
            map[x][y] ++, map[y][x] ++;
            a[x][y] ++, a[x][x] --, a[y][x] ++, a[y][y] --;
            x = find(q[i].x), y = find(q[i].y), par[x] = y;
        }
        for (int i = 1; i <= ts; i ++) {
            a[b[S]][i] = a[b[T]][i] = 0;
        }
        a[b[S]][b[S]] = 1, a[b[T]][b[T]] = 1, p[b[S]] = 1;
        if (find(S) != find(T)) { puts("inf"); continue ; }
        for (int i = 1; i <= ts; i ++) {
            int k = i;
            while (a[k][i] == 0 && k <= ts) k ++;
            if (k > ts) continue ;
            for (int j = 1; j <= ts; j ++) swap(a[i][j], a[k][j]);
            for (int j = i + 1; j <= ts; j ++) {
                double z = (a[j][i] / a[i][i]);
                for (int o = i; o <= ts; o ++) a[j][o] -= a[i][o] * z;
                p[j] -= p[i] * z;
            }
        }
        for (int i = ts; i >= 1; i --) {
            if (!a[i][i]) continue ;
            for (int j = i + 1; j <= ts; j ++) {
                p[i] -= a[i][j] * X[j];
            }
            X[i] = p[i] / a[i][i];
        }
        for (int i = 1; i <= ts; i ++) {
            ans += X[i] * map[i][b[T]];
        }
        printf("%.6f\n", 1 / ans);
    }
    
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值