bzoj2419 电阻 [高斯消元]

Description

你突破了无数艰难险阻,终于解决了上面那道题,众神犇瞬间就震惊了。他们发现居然有人可以把那种非人类做的题目做出来。他们一致同意,最后这道题不能再出数学题了。考虑到两位小盆友的状态,他们决定考考你的初中物理水平。 一个电路板,有 N 个接点,M 个电阻。电阻两端都在接点上,两位小盆友很聪明,他们拿出了一个欧姆表,瞬间就虐爆了…不过你也不甘落后,你肯定不会用这种投机取巧、误差巨大的方法,于是你看向了你的电脑。

Input

多组数据,输入直到文件结束
每组数据第一行两个整数N,M
接下来 M 行,每行三个非负整数 X,Y,R,表示电阻连接的两
接点和阻值。

Output

每组数据输出一行,一个实数,四舍五入到小数点后两位

Sample Input

2 1
1 2 1

Sample Output

1.00

Hint

0<N200
0<M50000

Solution

这题BZOJ居然没给数据范围!狂WA了若干次都没发现原因,坑得要命TAT

根据我们的物理知识 Iab=UR=ϕ(b)ϕ(a)Rab ,我们又知道对于**(除起始点外
)任意一个点流入的电流等于流出的电流**,于是我们可以令第一个点流出电流 I1=1 ,则最后一点流入电流 IN=1 ,再令第N个点电势 ϕ(N)=0 ,求出的 ϕ(1) 即为要求的等效电阻.第一个点的值为-1,最后一个点的值为1

Code

#include<bits/stdc++.h>
#define maxm 100010
#define maxn 1010
#define eps 1e-6
// #define DEBUG
using namespace std;
int n,m,num_edge;
double a[maxn][maxn];
int now[maxm],pre[maxm],to[maxm],deg[maxm],w[maxm];

int dcmp(double x)
{
  if(abs(x)<=eps) return 0;
  else return x>0?1:-1;
}

void add_edge(int u,int v,int wi)
{
  pre[++num_edge]=now[u];
  w[num_edge]=wi;
  now[u]=num_edge;
  to[num_edge]=v;
  deg[v]++;
}

inline int read()
{
  char ch;
  int sign=1,read=0;
  do
    ch=getchar();
  while((ch<'0'||ch>'9')&&ch!='-');
  if(ch=='-') sign=-1,ch=getchar();
  while(ch>='0' && ch<='9')
  {
    read=read*10+ch-'0';
    ch=getchar();
  }
  return sign*read;
}

void init()
{
  num_edge=0;
  memset(a,0,sizeof(a));
  for(int i=1;i<=max(n,m);++i)
    deg[i]=pre[i]=now[i]=to[i]=w[i]=0;
  for(int i=1,x,y,r;i<=m;++i)
    x=read(),y=read(),r=read(),add_edge(x,y,r),add_edge(y,x,r);
  for(int i=1;i<=n;++i)
    for(int x=now[i];x;x=pre[x])
    {
      a[i][to[x]]+=1.0/w[x];
      a[i][i]-=1.0/w[x];
    }
  a[1][n+1]=-1.0;
  a[n][n+1]=1.0;
  a[n][n]-=1.0;
}

void gauss()
{
  for(int i=1;i<=n;++i)
  {
    if(!dcmp(a[i][i]))
    {
      int k=i;
      while(!dcmp(a[k][k])) k++;
      swap(a[i],a[k]);
    }
    for(int j=i+1;j<=n;++j)
    {
      for(int k=i+1;k<=n+1;++k)
        a[j][k]-=a[i][k]*(a[j][i]/a[i][i]);
      a[j][i]=0;
    }
  }
  for(int i=n;i>1;--i)
    for(int j=1;j<i;++j)
    {
      a[j][n+1]-=a[i][n+1]*(a[j][i]/a[i][i]);
      a[j][i]=0;
    }
  printf("%.2lf\n",a[1][n+1]/a[1][1]+eps);
}

int main()
{
#ifdef DEBUG
  freopen("resistor.in","r",stdin);
/*#else
  freopen("resistor.in","r",stdin);
  freopen("resistor.out","w",stdout);*/
#endif
  while(scanf("%d%d",&n,&m)==2)
  {
    init();
#ifdef DEBUG
    for(int i=1;i<=n;++i)
    {
      cerr<<"i="<<i<<" -->";
      for(int p=now[i];p;p=pre[p])
        cerr<<to[p]<<" ";
      cerr<<";"<<endl;
    }
    for(int i=1;i<=n;++i){
      for(int j=1;j<=n+1;++j)
        printf("%.3lf ",a[i][j]);
      cout<<endl;}
#endif
    gauss();
  }
  return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值