【问题描述】
- 在复杂的武林中,有这样一个恶人:他身手不凡,经常去各武馆闹事,并以此为乐。他的存在,让每个武馆都不得安宁。
- 这一天,某武馆不幸收到了他的挑战书。为了维护武馆的名声和尊严,武馆主和武馆里的所有武士发誓要倾尽全力击败他。
- 武馆是由 NN 个直接或间接相通的房间构成的,房间标号为 0,1,……,N−1,某些房间之间有走廊可双向互通。通过研究恶人之前闹事的过程,武官主总结了他的一些特殊癖好:
- 在去武馆闹事之前,总是先深入研究过武馆的地图。
- 在武馆内走动的过程中会遵循下列规则:
- 总是从正厅(00 号房间)出发。
- 不会重复经过同一个房间。
- 他不想白白浪费自己的体力,因此任一时刻,若他之前经过的所有房间依次是 0,P1,P2,……,Pr,则这个序列一定是从 00 号房间到 Pr 号房间的最短路。
- 若到达某一点的两条路径长度相等,则他会从后向前依次比较这两条路径上的房间编号,直到找到不相同的编号,则编号小的房间所属的路径在他看来更短(从而另一条路径就一定不是最短路)。
- 在满足上述条件的情况下,下一步若有多个房间可以选择,他将等概率选取某一房间。
- 他将一直不停地移动,直到无法在上述条件下移动为止,这时,他就认为自己击败了整个武馆,然后大摇大摆地离开。
- 为了不让恶人顺利离开武馆,武馆主打算将 PP 个武士安排在某些房间,当恶人经过安排了武士的房间时,武士们和恶人会发生战斗。每个房间可以没有武士,也可以有一个甚至多名武士(以多打少?对付恶人就应该不择手段)。武馆主已经根据敌我的实力和各房间不同的环境制作了一张概率表,详细列举了在某个房间安排一定人数的武士时,在该房间里击败恶人的概率。
- 现在,他们想知道,击败恶人、保住武馆声誉的最大概率是多少。
【输入格式】
- 第一行是两个整数 n,m,表示武馆内的房间数和走廊数。接下来 mm 行,第 i 行是三个整数ai,bi,liai,bi,li,表示第 ii 个走廊连接房间 ai 和房间 bibi,长度为lili。之后一行是一个整数 pp,表示武士数。接下来 n 行,每行 pp 个 0 到 11 之间的实数(可以等于 0 或 11),描述了概率表 pt,其中第ii行第 j 个数为 pt[i][j]pt[i][j],表示在第 ii 个房间安排 j 名武士击败恶人的概率(安排 00 个武士时概率必然为 0,因此省略;安排人数越多,击败恶人的概率必然不会降低)。
【输出格式】
- 只有一个实数,表示击败恶人的概率(百分比,保留两位小数)。
【输入样例】
- 4 4
0 1 1
0 2 2
1 3 3
2 3 1
2
0.01 0.1
0.5 0.8
0.5 0.8
0.7 0.9
0 0
【输出样例】
【样例解释】
【数据规模】
- 30%30% 的数据,n≤10n≤10
- 60%60% 的数据,n≤100n≤100
- 100%100% 的数据,1≤n≤1000,0≤m≤10000,0≤p≤50,1≤li≤100001≤n≤1000,0≤m≤10000,0≤p≤50,1≤li≤10000
【分析】
- 由题意先跑个 SPFASPFA,求出起点到每个节点的最短路 dis[i]dis[i]。
- 对于节点 yy 的前一步是满足 dis[x]+lenx→y=dis[y] 中最小的 xx。
- 我们不妨把 x 看成 yy 的父亲,建出一棵树。
- 那么恶人被打败即为从根节点到任一叶子结点的路径,存在有一点的武士把恶人打败。
- 这样并不好做,考虑把问题转化:
- 设 f[x][i] 表示在节点 xx 放 i 个武士,恶人获胜的最小概率。
- 初值: f[x][i]=1f[x][i]=1(节点 xx 不放武士)。
- 转移:(用 f′[j] 记下转移前的 f[x][j]f[x][j],ss 表示当前子树个数)
- f[x][i+j]=min{s−1sf′[i]+f[y][j]s}(在子节点 yy 放 j 个武士)。
- f[x][i+j]=min{f′[i]×(1−pt[x][j])}f[x][i+j]=min{f′[i]×(1−pt[x][j])}(在节点 xx 放 j 个武士)。
- 答案: 1−f[0][p]1−f[0][p](表示击败恶人的最大概率) 。
- 时间复杂度 O(NP2)O(NP2)。
【代码】
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
inline int get()
{
char ch; int res = 0; bool flag = false;
while (!isdigit(ch = getchar()) && ch != '-');
(ch == '-' ? flag = true : res = ch ^ 48);
while (isdigit(ch = getchar()))
res = res * 10 + ch - 48;
return flag ? -res : res;
}
const int N = 1005, M = 2005, P = 55;
const int Maxn = 0x3f3f3f3f;
int n, m, p;
double pt[N][P], f[N][P], ff[P];
int dis[N], h[N * 100]; bool vis[N];
struct Edge
{
int to, cst; Edge *nxt;
};
Edge t[M], *lst[N], *T = t;
Edge q[M], *rst[N], *Q = q;
inline void Link(int x, int y, int z)
{
(++T)->nxt = lst[x]; lst[x] = T; T->to = y; T->cst = z;
(++T)->nxt = lst[y]; lst[y] = T; T->to = x; T->cst = z;
}
inline void Rink(int x, int y)
{
(++Q)->nxt = rst[x]; rst[x] = Q; Q->to = y;
}
template <class T> inline void CkMin(T &x, T y) {if (x > y) x = y;}
inline void Spfa()
{
memset(dis, Maxn, sizeof(dis));
int t = 0, w = 1, x, y; dis[h[1] = 0] = 0;
while (t < w)
{
vis[x = h[++t]] = false;
for (Edge *e = lst[x]; e; e = e->nxt)
if (dis[y = e->to] > dis[x] + e->cst)
{
dis[y] = dis[x] + e->cst;
if (!vis[y]) vis[h[++w] = y] = true;
}
}
for (int i = 1; i < n; ++i)
{
x = i; int res = Maxn;
for (Edge *e = lst[x]; e; e = e->nxt)
if (dis[x] == dis[y = e->to] + e->cst) CkMin(res, y);
Rink(res, x);
}
}
inline void TreeDp(int x)
{
for (Edge *e = rst[x]; e; e = e->nxt)
TreeDp(e->to);
int y, s = 0;
for (int i = 0; i <= p; ++i) f[x][i] = 1;
for (Edge *e = rst[x]; e; e = e->nxt)
{
++s; y = e->to;
for (int i = 0; i <= p; ++i) ff[i] = 1;
for (int i = 0; i <= p; ++i)
for (int j = 0, jm = p - i; j <= jm; ++j)
CkMin(ff[i + j], f[x][i] * (s - 1) / s + f[y][j] / s);
for (int i = 0; i <= p; ++i) f[x][i] = ff[i];
}
for (int i = 0; i <= p; ++i) ff[i] = f[x][i];
for (int i = 0; i <= p; ++i)
for (int j = 0, jm = p - i; j <= jm; ++j)
CkMin(f[x][i + j], ff[i] * (1 - pt[x][j]));
}
int main()
{
freopen("challenge.in", "r", stdin);
freopen("challenge.out", "w", stdout);
n = get(); m = get(); int x, y;
while (m--)
{
x = get(); y = get();
Link(x, y, get());
}
Spfa();
p = get();
for (int i = 0; i < n; ++i)
for (int j = 1; j <= p; ++j)
scanf("%lf", &pt[i][j]);
TreeDp(0);
printf("%.2lf", (1 - f[0][p]) * 100);
fclose(stdin); fclose(stdout);
return 0;
}