题目描述
呆子打算去上自习,虽然他不愿意。他来到五教门口时,发现他来迟了,事先又没有占位子,可能没有位子了。
根据经验,他知道每个小教室有位子的概率为p1,每个梯形教室有位子的概率为p2,每个扇形教室有位子得概率为p3。
由于他并不是真的想上自习,所以他并不打算到每个教室去找位子,而是选一条从前门走到后门的路径,路过一个教室就进去看看,如果有位子就只好自习,如果没有就继续向前,直到后门还没有找到位子就回去睡觉。
现在他希望你替他找到一条路径,使得他回去睡觉的概率最大。
输入格式
输入的第一行是1个整数n ( 1<=n<=100 ),表示五教共有n个教室,编号为1,2,3 ... n。
第2行是三个实数p1 p2 p3 ( 0 <= p1 p2 p3 <=1,精度为0.01 )。
第3到2+n为一列整数:
i c s1 s2 m a1 a2 a3 .... am
其中 i为教室编号。c 的值为0,1或2,分别表示i号教室为小/梯形/扇形教室。
若i号教室与前门直接相邻,则s1=1,否则s1=0。若i号教室与后门直接相邻,则s2=1,否则s2=0。
a1 a2 ..... am 是与第i个教室直接相邻的教室编号。所谓直接相邻,是指两个教室之间或大门与教室之间存在一条不经过其他教室或大门的路。保证至少存在一条从前门到后门的道路
输出格式
输出一行,包含一个实数,表示呆子回去睡觉的最大概率。四舍五入到0.01。
分析
刚拿到这题,看到有“概率”两字,自然想到概率Dp,但实际分析,本题是在无向图上的,若直接Dp,会有后效性。具体分析,可以发现,本题就是让我们求一条路径,使得路径上的点权乘积最大,因此可以联想的最短路径算法,改一下松弛条件和初始值就可以完美地A掉这道题。
代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <cmath>
using namespace std;
const int N=100005;
struct Edge {
int to,next;
}e[N*2];
int h[N],cnt;
int n,vis[N];
double p[3],v[N],ans,d[N];
void add(int x,int y) {
e[++cnt]=(Edge){y,h[x]};
h[x]=cnt;
}
int main() {
scanf("%d%lf%lf%lf",&n,&p[0],&p[1],&p[2]);
for (int i=1;i<=n;i++) {
int num,c;
scanf("%d%d",&num,&c);
v[num]=1-p[c];
int s1,s2;
scanf("%d%d",&s1,&s2);
if (s1) add(0,num);
if (s2) add(num,n+1);
int ct;
scanf("%d",&ct);
for (int j=1;j<=ct;j++) {
int a;
scanf("%d",&a);
add(a,num);
add(num,a);
}
}//稍微复杂的读入
v[0]=v[n+1]=1;
queue<int> q;
q.push(0);
vis[0]=1;
d[0]=1;//由于求最长路,初值直接为0即可
while (!q.empty()) {
int t=q.front();
q.pop();
vis[t]=0;
for (int i=h[t];i;i=e[i].next) {
int y=e[i].to;
if (d[y]<d[t]*v[y]) {//松弛条件
d[y]=d[t]*v[y];
if (!vis[y]) {
q.push(y);
vis[y]=1;
}
}
}
}
printf("%.2lf",d[n+1]);
return 0;
}