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

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_back
typedef 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(p[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();
	}
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-25 19:46:39

HDU5006 Resistance (2014年鞍山赛区网络赛J题)的相关文章

HDU 5002 Tree (2014年鞍山赛区网络赛F题)

1.题目描述:点击打开链接 2.解题思路:LCT的模板题 3.代码: #include <cstdio> #include <cstdlib> #include <algorithm> #include <iostream> #include <vector> using namespace std; const int N = 111111; const int INF = 1111111111; int n, m; class LCT { p

HDU5001 Walk (2014年鞍山赛区网络赛E题)

1.题目描述:点击打开链接 2.解题思路:本题利用矩阵快速幂+概率dp解决.根据题意可以画出来一个状态转移图,根据状态转移图不难得到一步转移概率矩阵,接下来的问题是:如何求解d步之内(包括d)均无法从其他点走到结点u的概率. 首先,既然无法到达结点u,那么出发的时候就不能选择该点.其次,为了使其他结点也无法到达结点u,可以将一步转移概率矩阵中跟结点u有关的概率全部置零.即表示u结点出发无法到达其他结点,其他结点也均无法到达u结点,这样就相当于把u结点从状态转移图中暂时删去了.然后根据马氏链的知识

HDU4998 Rotate (2014年鞍山赛区网络赛B题)

1.题目描述:点击打开链接 2.解题思路:本题属于几何变换专题,一开始想着随便枚举两个点,然后都进行一下旋转变换,最后利用原始点和旋转后的点所在直线的中垂线的交点求解.然而发现精度损失很大,而且可能有特殊情况没有考虑到.学习了一下几何变换的方法. 由于旋转操作相当于对一个点构成的矩阵和一个旋转矩阵做乘法运算.最基本的旋转变换就是任意一个点围绕原点进行逆时针旋转.如果改成围绕某个定点(x0,y0)进行旋转可以分解为三步:将被旋转点平移(-x0,-y0),进行基本旋转变换,再平移(x0,y0).在矩

HDU5003 Osu! (2014年鞍山赛区网络赛G题)

1.题目描述:点击打开链接 2.解题思路:本题是一道简单的排序题,按照题意排序计算即可. 3.代码: #define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<algorithm> #include<string> #include<sstream> #include<set> #include<vector> #include<stack> #include&

ZOJ 3818 Pretty Poem (2014年牡丹江赛区网络赛J题)

1.题目描述:点击打开链接 2.解题思路:本题是一道模拟题,输入一个串,要求判断是否形如"ABABA"或"ABABCAB".只需要对两种情况逐一尝试即可.然而这道题有诸多细节需要考虑.这里说一下我自己的方法. 首先,如果输入的串长度<5,那么直接输出No,或者去掉所有的标点后发现长度<5,输出No.长度上没问题后,写一个专门的solve(int type)函数,来判断是否是上述情况中的一种.对于第一种,只需要枚举A的长度即可,B的长度就是(len-3*i

HDU 4998 Rotate(计算几何)2014年鞍山赛区网络赛

Rotate                                                                           Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Special Judge Problem Description Noting is more interesting than rotation! Your litt

ZOJ 3810 A Volcanic Island (2014年牡丹江赛区网络赛B题)

1.题目描写叙述:点击打开链接 2.解题思路:本题是四色定理的模板题.只是有几种情况要提前特判一下:n==1直接输出,1<n<5时候无解,n==6时候套用模板会出现同样的块.因此要特判一下.其它情况都能直接利用模板构造出来. 3.代码: #define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<algorithm> #include<cassert> #include<string>

ZOJ 3814 Sawtooth Puzzle (2014年牡丹江赛区网络赛F题)

1.题目描写叙述:点击打开链接 2.解题思路:本题是一道隐式图的搜索题目.一般来说,这类题目首先要定义状态,接下来是弄清楚状态怎样转移,以及状态怎样判重,怎样推断当前状态是否和目标状态同样.至于求解最短路就是经常使用的BFS就可以. 接下来我们逐一展开讨论. 1.状态的定义:看到这道题,猛一下会想着把每一个字符分别用01表示,然后看成二进制码进行状态压缩,这个状态定义尽管能够,可是显然,状态过于精确和复杂,假设把一行给压缩成一个整数,那么一个完整的图案要用8*9.即72个数才干描写叙述.显然过于

西电校赛网络赛J题 lucas定理计算组合数

西电校赛网络赛J题  lucas定理计算组合数 问题 J: 找规律II 时间限制: 1 Sec  内存限制: 128 MB 提交: 96  解决: 16 [提交][状态][讨论版] 题目描述 现有数阵如下: 1    2  3   4     5    6 1   3   6  10  15 1   4  10   20 1   5   15 1    6 1 求这个数阵的第n行m列是多少(行列标号从1开始) 结果对10007取模 输入 多组数据,每组数据一行,包含两个整数n,m(1<=n<=