其他分享
首页 > 其他分享> > CF679D Bear and Chase

CF679D Bear and Chase

作者:互联网

CF679D Bear and Chase

题目链接

本题题解

记图的边集为 \(E\)。

先用 floyd 算法预处理出任意两点之间的距离。时间复杂度 \(\mathcal{O}(n^3)\)。

记两次查询的节点分别为 \(a\), \(b\),两次查询得到的结果分别为 \(d_1, d_2\)。

上述的做法看起来是四重循环。但对于同一个 \(a\),在所有 \(d_1\) 下 \(U\) 集合的大小之和为 \(n\)。\(V\) 集合里的点与 \(a\) 的距离要么是 \(d_1 + 1\),要么是 \(d_1 - 1\),所以 \(V\) 集合的大小之和也是 \(\mathcal{O}(n)\) 的。发现我们真正枚举的只有 \(a, b, V\),时间复杂度是 \(\mathcal{O}(n^3)\) 的。

具体可以见代码。

参考代码

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

#define mk make_pair
#define fi first
#define se second
#define SZ(x) ((int)(x).size())

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

template<typename T> inline void ckmax(T& x, T y) { x = (y > x ? y : x); }
template<typename T> inline void ckmin(T& x, T y) { x = (y < x ? y : x); }

const int MAXN = 400;
const int INF = 1e9;

int n, m;
vector<int> G[MAXN + 5];
int dis[MAXN + 5][MAXN + 5];

double p[MAXN + 5];
bool vis[MAXN + 5];
vector<int> calc_probability_for_each_city(const vector<int>& vec_u) {
	for (int i = 1; i <= n; ++i) {
		p[i] = 0;
		vis[i] = 0;
	}
	vector<int> vec_v;
	for (int i = 0; i < SZ(vec_u); ++i) {
		int u = vec_u[i];
		for (int j = 0; j < SZ(G[u]); ++j) {
			int v = G[u][j];
			p[v] += 1.0 / SZ(vec_u) / SZ(G[u]);
			if (!vis[v]) {
				vis[v] = 1;
				vec_v.push_back(v);
			}
		}
	}
	return vec_v;
}

double pmax[MAXN + 5];
double consider_tomorrow(const vector<int>& vec_v) {
	double res = 0;
	for (int i = 0; i < n; ++i) {
		pmax[i] = 0;
	}
	for (int b = 1; b <= n; ++b) { // 第二次询问的节点
		// 选好 b 后, 会问到一个距离 d2
		// 我们根据 d2 去选择最终的猜测: c. 即: 每个 d2 对应了一种选 c 的策略
		// 具体来说就是选择该 d2 里概率 p 最大的点作为 c
		// 所以此处先对每种距离 d2, 预处理出它对应的节点的 p 的最大值
		
		for (int i = 0; i < SZ(vec_v); ++i) {
			int v = vec_v[i];
			ckmax(pmax[dis[v][b]], p[v]);
		}
		
		double res_b = 0;
		for (int i = 0; i < SZ(vec_v); ++i) {
			int v = vec_v[i];
			int d2 = dis[v][b];
			
			res_b += pmax[d2];
			pmax[d2] = 0;
		}
		ckmax(res, res_b);
	}
	return res;
}
double ask_immediately(const vector<int>& vec_u) {
	return 1.0 / SZ(vec_u);
}

int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j)
			dis[i][j] = (i == j ? 0 : INF);
	for (int i = 1; i <= m; ++i) {
		int u, v;
		cin >> u >> v;
		G[u].push_back(v);
		G[v].push_back(u);
		dis[u][v] = dis[v][u] = 1;
	}
	for (int k = 1; k <= n; ++k) { // 中间点
		for (int i = 1; i <= n; ++i) { // 起点
			for (int j = 1; j <= n; ++j) { // 终点
				ckmin(dis[i][j], dis[i][k] + dis[k][j]);
			}
		}
	}
	double ans = 0;
	for (int a = 1; a <= n; ++a) { // 第一次询问的节点
		double ans_a = 0;
		for (int d1 = 0; d1 < n; ++d1) { // 第一次得到的答案
			vector<int> vec_u;
			double p_d1 = 0;
			for (int u = 1; u <= n; ++u) if (dis[a][u] == d1) {
				vec_u.push_back(u);
				p_d1 += 1;
			}
			if (!SZ(vec_u))
				continue;
			
			p_d1 /= (double)n;
			
			double ans_a_d1 = 0;
			vector<int> vec_v = calc_probability_for_each_city(vec_u);
			ans_a_d1 = max(consider_tomorrow(vec_v), ask_immediately(vec_u));
			ans_a += p_d1 * ans_a_d1; // 有 p 的概率, 进入 d1 这种情况
			                          // 在 d1 的情况下, 选取最优策略能得到 ans_a_d1 这一答案
		}
		ckmax(ans, ans_a); // 选取最优策略
	}
	cout << setiosflags(ios :: fixed) << setprecision(10) << ans << endl;
	return 0;
}

标签:CF679D,int,cap,Bear,枚举,MAXN,vec,Chase,d1
来源: https://www.cnblogs.com/dysyn1314/p/14347422.html