「LOJ6078」「2017 山东一轮集训 Day7」重排-DP

Description

链接

Solution

先假设没有自环。直接按拓扑序进行DP。

f i f_i 表式点 i i t t 的期望代价。

把出边与出点的 f f 两两匹配,形成 k 2 k^2 个二元组。然后考虑求出匹配方案的最小值 \ge 当前二元组的概率。算概率等同于算方案数。Rabbit Number即可。

如果有自环,发现题目要求保存 6 6 位小数,可以把当前的 f u f_u 丢进去迭代计算,直到误差很小时停止。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1005;

int n, m, s, t;

struct edge
{
	int to, next;
	double w;
} e[maxn * 2];
int h[maxn], tot;

bool vis[maxn], to[maxn];
double f[maxn];

int cnt_a, cnt_b, cnt_c, cnt_p, cnt_q, cnt_r;
double a[maxn], b[maxn];
pair<double, int> p[maxn * maxn], q[maxn * maxn], r[maxn * maxn];
int cnt[maxn];

inline int gi()
{
	char c = getchar();
	while (c < '0' || c > '9') c = getchar();
	int sum = 0;
	while ('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
	return sum;
}

inline void add(int u, int v, double w)
{
	e[++tot] = (edge) {v, h[u], w};
	h[u] = tot;
}

void pre(int u)
{
	cnt_a = cnt_b = cnt_c = cnt_p = 0;
	for (int i = h[u]; i; i = e[i].next) a[++cnt_a] = e[i].w;
	for (int i = h[u], v; v = e[i].to, i; i = e[i].next) if (v != u) b[++cnt_b] = f[v]; else ++cnt_c;
	sort(a + 1, a + cnt_a + 1);
	sort(b + 1, b + cnt_b + 1);
	for (int i = 1; i <= cnt_a; ++i)
		for (int j = 1; j <= cnt_b; ++j)
			p[++cnt_p] = make_pair(a[i] + b[j], j);
	sort(p + 1, p + cnt_p + 1);
}

double calc(double y)
{
	int t = 0;
	while (t < cnt_b && b[t + 1] < y) ++t;
	for (int i = 1; i <= cnt_p; ++i) if (p[i].second > t) p[i].second += cnt_c;
	cnt_q = 0;
	for (int i = 1; i <= cnt_a; ++i)
		for (int j = 1; j <= cnt_c; ++j)
			q[++cnt_q] = make_pair(a[i] + y, j + t);
	merge(p + 1, p + cnt_p + 1, q + 1, q + cnt_q + 1, r + 1); cnt_r = cnt_p + cnt_q;
	for (int i = 1; i <= cnt_p; ++i) if (p[i].second > t) p[i].second -= cnt_c;

	for (int i = 1; i <= cnt_a; ++i) cnt[i] = cnt_a - i + 1;
	double P = 1, res = 0, lst = 0;
	for (int i = 1, x; i <= cnt_r; ++i) {
		res += P * (r[i].first - lst); lst = r[i].first;
		x = r[i].second;
		P = P / cnt[x] * (cnt[x] - 1); --cnt[x];
		if (P < 1e-9) break;
	}
	return res;
}

double dfs(int u)
{
	if (vis[u]) return f[u];
	vis[u] = 1;
	for (int i = h[u], v; v = e[i].to, i; i = e[i].next)
		if (v != u) dfs(v), to[u] |= to[v];
	if (!to[u]) f[u] = 1e9;
	else {
		pre(u);
		if (!cnt_c) f[u] = calc(0);
		else {
			double lst = 1e9 + 1; f[u] = 1e9;
			while (lst - f[u] > 1e-6) {
				lst = f[u];
				f[u] = calc(lst);
			}
		}
	}
	return f[u];
}

int main()
{
	//freopen("shuffle5.in", "r", stdin);
	freopen("shuffle.in", "r", stdin);
	freopen("shuffle.out", "w", stdout);

	n = gi(); m = gi(); s = gi(); t = gi();

	double c;
	for (int u, v, i = 1; i <= m; ++i) {
		u = gi(); v = gi(); scanf("%lf", &c);
		add(u, v, c);
	}

	vis[t] = to[t] = 1;
	dfs(s);
	if (!to[s]) puts("-1");
	else printf("%.6lf", f[s]);
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/DSL_HN_2002/article/details/85610802
今日推荐