HDU 5006 Resistance (2014年鞍山赛区网络赛J题)

来源:互联网 发布:java 启动main方法 编辑:程序博客网 时间:2024/05/20 18:47

1.题目描述:点击打开链接

2.解题思路:本题利用缩点+高斯消元解决。本题的最大特点就是电阻非零即一,如果电阻为0,说明零点之间是等电位点,可以看做一个整体,自然可以想到先利用并查集进行缩点操作,将复杂的电路图转化为不相等的电位点构成的电路图。如果转换完毕后,发现s和t在一个集合中,说明两点之间是等电位的,等效电阻为0,否则,对转换后的图G'重新判断连通性,依然可以利用并查集解决,如果发现不连通,说明s与t之间开路,电阻为inf,否则,就可以根据tot个点的电位列写方程。

        我们令有1A的电流从点s流出,即点s对应的第id(s)行方程等式右端为1(对应于代码中的A[s][tot]=1),同理,电流最终从点t流入,则第id(t)行方程的右端应该为-1(对应于代码中的A[t][tot]=-1)。如果知道了s,t两点的电位,那么等效电阻就是s,t两点的电位差(因为电流为1A)。

       接下来问题转换为如何列写方程?根据基尔霍夫电流定律可知,从每个结点流出的电流值和为0,根据电路基础中的“自电阻”和“互电阻”的概念可知,如果转换后的图G'中,点x,y相连,则自电阻A[x][x]++,A[y][y]++,,互电阻A[x][y]--,A[y][x]--。由电路分析基础的知识可知,n个结点只能列写n-1个独立的方程,换句话说,进行对增广矩阵进行高斯消元后,一定会出现某一全零行,因此必须指定一个]电位参考点。不妨将s点当做零电位点,即让第id(s)个未知数=0(即s点为人为规定的零电位点),那么只需要把它的系数改为一个非零值即可,由于在高斯消元中总是让最后一行为全零行,因此令A[tot-1][id(s)]++,即可实现零电位点的设置。至此,方程全部列写完毕,利用高斯消元法求解所有未知量即可。最后的结果为A[id(s)][tot]-A[id(t)][tot]+eps(防止出现误差)。

3.代码:

#define _CRT_SECURE_NO_WARNINGS#include<iostream>#include<algorithm>#include<string>#include<sstream>#include<set>#include<vector>#include<stack>#include<map>#include<queue>#include<deque>#include<cstdlib>#include<cstdio>#include<cstring>#include<cmath>#include<ctime>#include<cctype>#include<functional>using namespace std;#define me(s) memset(s,0,sizeof(s))#define pb push_backtypedef long long ll;typedef unsigned int uint;typedef unsigned long long ull;typedef pair <int, int> P;const int N = 10000 + 5;const double eps = 1e-8;int p[N];int find(int x){return p[x] == x ? x : p[x] = find(p[x]);}int u[4 * N], v[4 * N], c[4 * N];int id[N], tot;double A[500][500];int n, m, s, t;void input(){scanf("%d%d%d%d", &n, &m, &s, &t);for (int i = 0; i<m; i++)scanf("%d%d%d", u + i, v + i, c + i);}void gauss(int n)//高斯消元法{int i, j, k, r;for (i = 0; i<n; i++){r = i;for (j = i + 1; j<n; j++)if (fabs(A[j][i])>fabs(A[r][i]))r = j;if (r != i)for (j = 0; j <= n; j++)swap(A[r][j], A[i][j]);for (j = i + 1; j <= n; j++)A[i][j] /= A[i][i];A[i][i] = 1.0;for (k = 0; k<n; k++){if (fabs(A[k][i])<eps || i == k)continue;double f = A[k][i];for (j = 0; j <= n; j++)A[k][j] -= f*A[i][j];}}}void solve(){for (int i = 1; i <= n; i++)p[i] = i;for (int i = 0; i<m; i++)if (!c[i])p[find(u[i])] = find(v[i]);//说明u[i],v[i]为等电位点,利用并查集合并,实现缩点if (find(s) == find(t))//缩点后,如果发现s,t在同一个集合,说明等电位,即等效电阻为0{printf("0.000000\n");return;}tot = 0;//否则,对缩点后新图逐一编号for (int i = 1; i <= n; i++)if (find(i) == i)id[i] = tot++;//先将并查集的根找出来并按照顺序编号for (int i = 1; i <= n; i++)id[i] = id[find(i)];//再标记其他点,均标记为他们的根对应的编号for (int i = 0; i<tot; i++)p[i] = i;//对新图进行连通性的判断for (int i = 0; i<m; i++)p[find(u[i])] = find(v[i]);//如果u[i],v[i]相连(此时电阻都是1欧姆),则将对应的并查集合并if (find(s) != find(t))//新图不连通,说明s,t之间开路{puts("inf"); return;}me(A);//新图连通,准备逐一判断系数大小for (int i = 0; i<m; i++){if (id[u[i]] == id[v[i]])continue;//等电位点,跳过int x = id[u[i]], y = id[v[i]];//先找到他们对应的编号++A[x][x], --A[x][y];//自电阻增加,互电阻减小++A[y][y], --A[y][x];}s = id[s], t = id[t];//分别将源点s,汇点t设置为他们对应的编号A[s][tot] = 1.0, A[t][tot] = -1.0;//s点流出1A的电流,t点流入1A的电流A[tot - 1][s]++;//设置s点为零电位点gauss(tot);//通过高斯消元法求解所有的n个电位点printf("%.6lf\n", -A[t][tot] + A[s][tot] + eps);//等效电阻为s,t之间的电位差(因为电流为1A),加上eps防止出现精度问题}int main(){int T;scanf("%d", &T);while (T--){input();solve();}}

0 0