当先锋百科网

首页 1 2 3 4 5 6 7

初见安~这是近日连测的考试题目所以我也不知道出处QwQ

题目描述

  每次小x物理作业没做完时,总是会去和老师交流感情,他们之间由此建立起来良好的师生关系。于是有一天,老师带着一道物理难题来见小x。

  这道题给出了一个有n个电阻的电路,每个电阻使用一个大写字母来按字典序依次编号,一个或者多个电阻之间串联、并联或者混联构成了电路,电阻自身就是最小的电路。

  老师说:“现在告诉了你各电路之间的关系和每个电路断路的概率,请你求出整个电路断路的概率。”

  注意,我们可以视所有的电阻组成的一个总电路两端被加上了电压,当总电路不产生电流时视为整个电路断路,若总电路为若干电路串联,则任一子电路断路视为总电路断路;若总电路为若干电路并联,则并联的所有电路断路视为总电路断路;若总电路为混联,则任一混联子电路断路视为总电路断路。

样例输入

5
(A,B)((C)(D),E)
0.2
0.3
0.4
0.5
0.6

样例输出

0.2992

题解

这个题吧……其实很好想到的就是:串联【S集合】挂掉一个就全挂掉了,也就是只有全都不挂才能不断路;并联【T集合】,全部挂掉才算挂掉。所以各自的断电概率为:

串联:1-\prod {(1-p_i)},i\in S, 并联:\prod {p_i}, i \in T

所以这个题真正的难点在于——怎么处理题目给出的那个括号字符串。

可以看出——并联的优先级大于串联,因为并联有类似于总和的意思。嘛,看这个字符串的话,我们肯定是从括号入手。可以想到类似于dfs分解各个区间的做法……【似乎不可行?反正我没写出来QwQ】

正解做法是:就像处理四则运算的表达式一样,如果是并联的话,我们需要注意在括号之间加一个符号以辨认,类似于'*'。

接下来看处理的思路:

我们涉及到的符号有:前后括号,乘号和逗号,如果遇到了后括号就要去匹配前括号并且让括号内的内容算出答案。然而如果遇到了括号才计算的话很麻烦,所以我们边扫边算。遇到了字母,就把这个电阻的概率放进数组s2,;如果是上述符号,就放进数组s1,遇到了后括号或者另外两种操作符的时候就把s2的值取出来用,顺便覆盖下,符号也跟着在s1里面往前面删。

当然如果是前括号的话直接扔进s1里面就可以了。

为了添加'*'的操作方便,我们把读入的s数组处理到了ss数组里。

代码有参考ls大佬】

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define maxn 100
using namespace std;
typedef double ll;
int read() {
	int x = 0, f = 1, ch = getchar();
	while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
	while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
	return x * f;
}

int n, t1 = 0, t2 = 0, m = 0;
ll p[maxn];
char s[maxn], ss[maxn], s1[maxn];
ll s2[maxn];

ll cal(ll a, ll b, char c) {
	if(c == '*') return a * b;
	else return a + b - a * b;//因为涉及到的都是 两两操作,所以简化后:1-(1-x)*(1-y)=1-(1+x*y-x-y) 
}

void work() {
	ll x;
	for(int i = 1; i <= m; i++) {
		if(ss[i] >= 'A' && ss[i] <= 'Z') s2[++t2] = p[ss[i] - 'A'];//把数值放进去 
		else if(ss[i] == ')') {while(s1[t1] != '(') x = cal(s2[t2], s2[t2 - 1], s1[t1--]), s2[--t2] = x; t1--;}//后括号就去找前括号 
		else if(ss[i] == '@') {while(t1) x = cal(s2[t2], s2[t2 - 1], s1[t1--]), s2[--t2] = x;}//到底了,开始清空之前存下来的操作符 
		else if(ss[i] == ',') {while(s1[t1] == '*') x = cal(s2[t2], s2[t2 - 1], s1[t1--]), s2[--t2] = x;  s1[++t1] = ss[i];}//
		else if(ss[i] == '*') {while(s1[t1] == '*') x = cal(s2[t2], s2[t2 - 1], s1[t1--]), s2[--t2] = x;  s1[++t1] = ss[i];}//这两行操作一样的 
		else s1[++t1] = ss[i]; //再如果就是前括号了 
		//注意,上方都是先计算出cal()再赋值给的s2[--t2],这里t2的值不能提前减一。 
	}
}

signed main() {
	n = read(); scanf("%s", s + 1);
	for(int i = 0; i < n; i++) scanf("%lf", &p[i]);
	
	for(int i = 1; i <= strlen(s + 1); i++) {
		if(s[i] == '(' && s[i - 1] == ')') ss[++m] = '*';
		ss[++m] = s[i];//处理s数组到ss 
	}
	
	ss[++m] = '@'; //s随便加个符号表示是最后一个了 
	work();
	printf("%.4lf\n", s2[1]);
	return 0;
}

迎评QwQ

——End——