hdu3976[Electric resistance] hdu5006[Resistance] hdu4870[Rating] 高斯消元
2014-11-15 16:21
267 查看
写了几个高斯消元的题,试一下模板
hdu3976
题意:给定50个节点,2000个电阻,求1到n的等效电阻
这题要用
节点电位法。
在1和n之间加一个1A的电流源,将n接地,这样求出的1号点电位数值上等于等效电阻。
hdu5006
题意不变,求s到t之间的等效电阻,但是最多有10000个点,电阻的值只能随机取0或1
两节点之间电阻如果为0说明可以看做一个点,所以实际上点的数量非常少。
另外如果s和t在电路中不联通,则方程无解。
hdu4870
概率dp,50分看成1,1000分就成了20
dp[i][j] 表示一个账号为i 另一个为j 时的期望数
转移方程 dp[i][j] = p*dp[i+1][j] + (1-p)*dp[i-2][j] + 1
方程既跟后面的状态有关,又跟前面的状态有关,所以不能直接递推
这种题就要用高斯消元。
ps:写这题的时候有两个地方卡了很久
[b]1、我用 q 代替 1-p ,导致了精度损失。
2、原来写高斯消元我会取绝对值最大的主元进行消元,据说可以减少精度损失。
但由于未知原因,这一题加了这个优化反而会损失精度。
hdu3976
题意:给定50个节点,2000个电阻,求1到n的等效电阻
这题要用
节点电位法。
在1和n之间加一个1A的电流源,将n接地,这样求出的1号点电位数值上等于等效电阻。
#include <cstdio> #include <iostream> typedef long long ll; using namespace std; void in(int &x){ char ch; int minus = 0; while (ch=getchar(), (ch<'0'||ch>'9') && ch!='-'); if (ch == '-') minus = 1, x = 0; else x = ch-'0'; while (ch=getchar(), ch>='0'&&ch<='9') x = x*10+ch-'0'; if (minus) x = -x; } void in(ll &x){ char ch; int minus = 0; while (ch=getchar(), (ch<'0'||ch>'9') && ch!='-'); if (ch == '-') minus = 1, x = 0; else x = ch-'0'; while (ch=getchar(), ch>='0'&&ch<='9') x = x*10+ch-'0'; if (minus) x = -x; } void out(int x){ char hc[30];int len, minus=0; if (x<0) minus = 1, x = -x; len = 0; hc[len++] = x%10+'0'; while (x/=10) hc[len++] = x%10+'0'; if (minus) putchar('-'); for (int i=len-1; i>=0; i--) putchar(hc[i]); } void out(ll x){ char hc[30];int len, minus=0; if (x<0) minus = 1, x = -x; len = 0; hc[len++] = x%10+'0'; while (x/=10) hc[len++] = x%10+'0'; if (minus) putchar('-'); for (int i=len-1; i>=0; i--) putchar(hc[i]); } #include <cstring> #include <cmath> const int N = 50+5; const double eps = 1e-6; double g ; int n, m; void solve(int n, double g[] ){ int now = 0; for (int c=0; c<n; c++){ int k = -1; for (int i=now; i<n; i++) if (fabs(g[i][c])>eps) if (k==-1 || fabs(g[i][c])>fabs(g[k][c])) k = i; if (k == -1) continue; if (k != now) for (int j=c; j<=n; j++) swap(g[now][j], g[k][j]); for (int i=0; i<n; i++) if (i!=now && fabs(g[i][c])>eps){ double tmp = g[i][c]/g[now][c]; for (int j=c; j<=n; j++) g[i][j] -= tmp*g[now][j]; } now ++; } for (int i=0; i<n; i++) if (fabs(g[i][i])>eps) g[i] /= g[i][i]; } int main(){ #ifndef ONLINE_JUDGE freopen("input.txt", "r", stdin); #endif int test; in(test); for (int T=1; T<=test; T++){ in(n); in(m); memset(g, 0, sizeof(g)); int a, b, c; for (int i=1; i<=m; i++){ in(a); in(b); in(c); if (!c) continue; a--; b--; double tmp = 1.0/c; g[a][a] += tmp; g[b] += tmp; g[a][b] -= tmp; g[b][a] -= tmp; } for (int i=0; i<n; i++) g[i] = 0.0; g[0] = 1.0; g[n-1] = -1.0; solve(n, g); printf("Case #%d: %.2lf\n", T, g[0] -g[n-1] ); } return 0; }
hdu5006
题意不变,求s到t之间的等效电阻,但是最多有10000个点,电阻的值只能随机取0或1
两节点之间电阻如果为0说明可以看做一个点,所以实际上点的数量非常少。
另外如果s和t在电路中不联通,则方程无解。
#include <cstdio> #include <iostream> typedef long long ll; using namespace std; void in(int &x){ char ch; int minus = 0; while (ch=getchar(), (ch<'0'||ch>'9') && ch!='-'); if (ch == '-') minus = 1, x = 0; else x = ch-'0'; while (ch=getchar(), ch>='0'&&ch<='9') x = x*10+ch-'0'; if (minus) x = -x; } void in(ll &x){ char ch; int minus = 0; while (ch=getchar(), (ch<'0'||ch>'9') && ch!='-'); if (ch == '-') minus = 1, x = 0; else x = ch-'0'; while (ch=getchar(), ch>='0'&&ch<='9') x = x*10+ch-'0'; if (minus) x = -x; } void out(int x){ char hc[30];int len, minus=0; if (x<0) minus = 1, x = -x; len = 0; hc[len++] = x%10+'0'; while (x/=10) hc[len++] = x%10+'0'; if (minus) putchar('-'); for (int i=len-1; i>=0; i--) putchar(hc[i]); } void out(ll x){ char hc[30];int len, minus=0; if (x<0) minus = 1, x = -x; len = 0; hc[len++] = x%10+'0'; while (x/=10) hc[len++] = x%10+'0'; if (minus) putchar('-'); for (int i=len-1; i>=0; i--) putchar(hc[i]); } #include <cstring> #include <cmath> #include <cassert> const int N = 2500; const int M = 40000+10; const double eps = 1e-7; int n, m, st, ed; double g , ans ; int fa[N*4+10], u[M], v[M], c[M], ms[M/4]; int find(int x){ if (fa[x] == x) return x; return fa[x] = find(fa[x]); } bool solve(int n, double g[] , double ans[]){ int now = 0; for (int c=0; c<n; c++){ int r = now; for (int i=now; i<n; i++) if (fabs(g[i][c]) > fabs(g[r][c])) r = i; if (r != now) for (int j=c; j<=n; j++) swap(g[r][j], g[now][j]); if (fabs(g[now][c]) < eps) continue; for (int i=0; i<n; i++) if (i != now && fabs(g[i][c])>eps){ double tmp = g[i][c]/g[now][c]; for (int j=c; j<=n; j++) g[i][j] -= tmp*g[now][j]; } now ++; } for (int j=0; j<n; j++) for (int i=0; i<n; i++) if (fabs(g[i][j]) > eps){ ans[j] = g[i] /g[i][j]; break; } for (int i=0; i<n; i++) if (fabs(g[i] )>eps){ int flag = 0; for (int j=i; j<n; j++) if (fabs(g[i][j])>eps){ flag = 1; break; } if (!flag) return false; } return true; } int main(){ #ifndef ONLINE_JUDGE freopen("input.txt", "r", stdin); #endif int test, start, finish; in(test); while (test--){ in(n); in(m); in(start); in(finish); memset(ms, 0xff, sizeof(ms)); memset(g, 0, sizeof(g)); for (int i=1; i<=n; i++) fa[i] = i; for (int i=1; i<=m; i++){ in(u[i]); in(v[i]); in(c[i]); if (!c[i]){ int l = find(u[i]), r = find(v[i]); if (l!=r) fa[l] = r; } } int cnt = 0; for (int i=1; i<=n; i++) if (find(i) == i) ms[i] = cnt++; else ms[i] = -1; for (int i=1; i<=m; i++) if (c[i]){ int l = find(u[i]), r = find(v[i]); if (l != r){ l = ms[l]; r = ms[r]; g[l][l] += 1; g[r][r] += 1; g[l][r] -= 1; g[r][l] -= 1; } } st = ms[find(start)]; ed = ms[find(finish)]; for (int i=0; i<cnt; i++) g[i][ed] = 0; g[st][cnt] = 1; g[ed][cnt] = -1; if (st == ed) printf("%.6lf\n", 0.0);//important else if (solve(cnt, g, ans)) printf("%.6lf\n", ans[st]); else puts("inf"); } return 0; }
hdu4870
概率dp,50分看成1,1000分就成了20
dp[i][j] 表示一个账号为i 另一个为j 时的期望数
转移方程 dp[i][j] = p*dp[i+1][j] + (1-p)*dp[i-2][j] + 1
方程既跟后面的状态有关,又跟前面的状态有关,所以不能直接递推
这种题就要用高斯消元。
ps:写这题的时候有两个地方卡了很久
[b]1、我用 q 代替 1-p ,导致了精度损失。
2、原来写高斯消元我会取绝对值最大的主元进行消元,据说可以减少精度损失。
但由于未知原因,这一题加了这个优化反而会损失精度。
#include <cstdio> #include <cstring> #include <cmath> #include <cassert> #include <algorithm> using namespace std; const double eps = 1e-9; const int LIM = 210; const int L = 20; int n; double p, q; double g[LIM+5][LIM+5];//, gg[LIM+5][LIM+5]; int id[L+5][L+5], pre[LIM+5], next[LIM+5]; double solve(int n, double g[][LIM+5], double ans[]){ int now = 0, r; for (int c=0; c<n; c++){ int r = now; for (int i=now; i<n; i++) if (fabs(g[i][c]) > eps && fabs(g[i][c]) < fabs(g[r][c])) r = i; for (r=now; r<n; r++) if (fabs(g[r][c])>eps) break; if (r != now) for (int j=c; j<=n; j++) swap(g[r][j], g[now][j]); if (fabs(g[now][c]) < eps) continue; for (int i=0; i<n; i++) if (i != now && fabs(g[i][c])>eps){ double tmp = g[i][c]/g[now][c]; for (int j=c; j<=n; j++) g[i][j] -= tmp*g[now][j]; } now ++; } return g[0] /g[0][0]; } void calc(int r, int c){ int tr = max(r-2, 0), tc = c; pre[id[r][c]] = id[tc]; tr = r+1; if (tr>tc) swap(tr, tc); if (tc == L) return; next[id[r][c]] = id [tc]; } int main(){ #ifndef ONLINE_JUDGE freopen("input.txt", "r", stdin); #endif memset(pre, 0xff, sizeof(pre)); memset(next, 0xff, sizeof(next)); for (int i=0, cnt = 0; i<L; i++) for (int j=i; j<L; j++) id[i][j] = cnt++; for (int i=0; i<L; i++) for (int j=i; j<L; j++){ calc(i, j); } while (scanf("%lf", &p) != EOF){ memset(g, 0, sizeof(g)); q = 1.0-p; for (int i=0; i<L; i++) for (int j=i; j<L; j++){ int k = id[i][j]; g[k][k] += 1; if (pre[k] != -1) g[k][pre[k]] -= (1-p); if (next[k] != -1) g[k][next[k]] -= p; g[k][LIM] += 1; } double ans[LIM+10]; printf("%.6lf\n", solve(LIM, g, ans)); } return 0; }
这一题还有另一种思路
原来状态方程是dp[i][j] = p*dp[i+1][j] + (1-p)*dp[i-2][j] + 1 ,跟前面、后面的状态都有关。
其实两个账号可以分开来看,一个账号你要达到20, 另一个则要达到19
所以可以用dp[i]表示从i到i+1所需期望步数,
转移方程为 dp[i] = p*1+(1-p)*(dp[i-2]+dp[i-1]+dp[i]),
这样就只涉及前面的状态了,可以直接o(n)的dp#include <cstdio> using namespace std; const int N = 21; double p; double dp , s ; int main(){ double p; while (scanf("%lf", &p) != EOF){ dp[0] = 1/p; dp[1] = 1/p/p; for (int i=2; i<=20; i++){ dp[i] = (1/p-1)*(dp[i-2]+dp[i-1]) + 1/p; } s[0] = dp[0]; for (int i=1; i<=20; i++) s[i] = s[i-1]+dp[i]; printf("%.6lf\n", s[19]+s[18]); } return 0; }相关文章推荐
- hdu4870 Rating高斯消元
- HDU5006 Resistance(高斯消元)
- hdu4870 Rating (高斯消元或者dp)
- hdu4870 Rating (高斯消元或者dp)
- HDU4870_Rating_双号从零单排_高斯消元求期望
- HDU 4870 Rating(概率dp+高斯消元)
- hdu 4870 Rating(高斯消元求期望)
- HDU 4870 Rating 多校联合练习赛 高斯消元
- [hdu4870]高斯消元
- HDU 4870 Rating 2014 Multi-University Training Contest 1 J题 概率DP+高斯消元
- POJ3532_Resistance_高斯消元&&基尔霍夫定律(高中物理)
- HDU 4870 Rating 高斯消元求期望
- hdu4870 高斯消元
- POJ 3532 Resistance(高斯消元+基尔霍夫定理)
- HDU 5006 Resistance 缩点 + 高斯消元
- hihocoder 1166 交换代数|HDU 4870 Rating 等(区间翻转,高斯消元求期望)
- hdu 4870 Rating 高斯消元/递推
- 2014多校第一场J题 || HDU 4870 Rating(DP || 高斯消元)
- hdu--4870 Rating(高斯消元,概率)
- HDU 4870 Rating 概率DP(高斯消元)