[NOI2005]聪聪与可可 解题报告

这篇博客详细介绍了NOI2005竞赛中的一道题目,该题目涉及图论和期望计算。作者通过解析题目大意,阐述了解题思路,利用二维动态规划和最短路算法解决猫鼠追逐问题。博客中还包含了代码实现和复杂度分析,最后讨论了算法的时间复杂度,指出全局最短路径的求解是主要耗时部分,整体复杂度大约为O(n^2)。
摘要由CSDN通过智能技术生成

[NOI2005]聪聪与可可 解题报告

定位:一道期望+图论好题。

题目链接

P4206 NOI2005 聪聪与可可 - 洛谷

题目大意

太长了没有。

但数据范围是 1 ≤ N , E ≤ 1000 1\le N,E\le 1000 1N,E1000

解题报告

我们发现不仅鼠在动,猫也在动,所以不妨设二维dp, f ( a , b ) f(a,b) f(a,b) 表示猫在 a a a ,老鼠在 b b b ,并且猫先走,猫抓住老鼠的期望步数。

n s ( a , b ) ns(a,b) ns(a,b) 表示猫在 a a a ,老鼠在 b b b ,猫下一步走到哪。于是有以下转移:

  • 如果猫和老鼠重合,即 a = b a=b a=b ,则 f ( a , b ) = 0 f(a,b)=0 f(a,b)=0.
  • 如果猫走一步或两步就能抓住鼠,那么鼠连走的机会都没有,那么只需一步。即若 n s ( a , b ) = b ns(a,b)=b ns(a,b)=b n s ( n s ( a , b ) , b ) = b ns(ns(a,b),b)=b ns(ns(a,b),b)=b ,则 f ( a , b ) = 1 f(a,b)=1 f(a,b)=1
  • 如果猫这一次还抓不住鼠,那么转移就变得复杂了。是下面这个式子:

f ( a , b ) = [ ∑ k f ( n s ( n s ( a , b ) , b ) , k ) ] / ( d e g [ b ] + 1 ) + 1 f(a,b)=[\sum_kf(ns(ns(a,b),b),k)]/(deg[b]+1) +1 f(a,b)=[kf(ns(ns(a,b),b),k)]/(deg[b]+1)+1

其中 k k k 是从 b b b 一部可跳到的点,包括 b b b 本身

容易发现这个转移是没有后效性的。


那么关键在于,如何求出任意一对 a , b a,b a,b n s ns ns 来呢?

由于 n s ns ns 一定在 a → b a\to b ab 的最短路上,我们先求全局最短路,就可以通过枚举 a a a 可到达的顶点,看看它在不在最短路上,选个标号最小的就行。

感觉复杂度假了?我们先看代码,再做复杂度分析。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef long double ldb;
char In[1 << 20], *ss = In, *tt = In;
#define getchar() (ss == tt && (tt = (ss = In) + fread(In, 1, 1 << 20, stdin), ss == tt) ? EOF : *ss++)
ll read() {
	ll x = 0, f = 1; char ch = getchar();
	for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;
	for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + int(ch - '0');
	return x * f;
}
const int MAXN = 1005;
const int MAXE = 1005;
int N, E;
int C, M;
int head[MAXN], ver[MAXE * 2], nxt[MAXE * 2], e_num, deg[MAXN];
void addedge(int u, int v) {
	ver[++e_num] = v; nxt[e_num] = head[u]; head[u] = e_num;
}
int dis[MAXN][MAXN], vis[MAXN];
int que[MAXN], hd, tl;
void bfs(int s, int dis[]) {
	memset(vis, 0x00, sizeof vis);
	hd = 1, tl = 0;
	que[++tl] = s;
	vis[s] = 1; dis[s] = 0;
	while(hd <= tl) {
		int u = que[hd++];
		for(int i = head[u]; i; i = nxt[i]) {
			int v = ver[i]; if(vis[v]) continue;
			vis[v] = 1;
			dis[v] = dis[u] + 1;
			que[++tl] = v;
		}
	}
}
int ns[MAXN][MAXN];
ldb f[MAXN][MAXN];
bool vis2[MAXN][MAXN];
ldb dfs(int a, int b) {//cat first
	if(a == b) return 0;
	if(ns[a][b] == b || ns[ns[a][b]][b] == b) return 1;
	if(vis2[a][b]) return f[a][b];
	vis2[a][b] = 1; f[a][b] = 0;
	int nns = ns[ns[a][b]][b];
	for(int i = head[b]; i; i = nxt[i]) {
		int v = ver[i];
		f[a][b] += dfs(nns, v);
	}
	f[a][b] += dfs(nns, b);
	f[a][b] /= deg[b] + 1;
	f[a][b]++;
	return f[a][b];
}

int main() {
	N = read(), E = read(), C = read(), M = read();
	for(int i = 1; i <= E; i++) {
		int u = read(), v = read();
		addedge(u, v); addedge(v, u);
		deg[u]++; deg[v]++;
	}
	for(int i = 1; i <= N; i++) {
		bfs(i, dis[i]);
	}
	memset(ns, 0x3f, sizeof ns);
	for(int i = 1; i <= N; i++)
		for(int j = 1; j <= N; j++) if(i != j)
			for(int k = head[i]; k; k = nxt[k]) {
				int t = ver[k];
				if(dis[i][j] == dis[t][j] + 1) ns[i][j] = min(ns[i][j], t);//t is in the shortest path of (i, j)
			}
	printf("%.3Lf\n", dfs(C, M));
	return 0;
}

复杂度分析

首先一个大头就是全局最短路。由于边权都为1,我们可以跑 n n n 次bfs,复杂度 O ( n ( n + m ) ) O(n(n+m)) O(n(n+m)).

然后是求 n s ns ns 数组。枚举点是 O ( n 2 ) O(n^2) O(n2) 的,而每条边都会被枚举 O ( 1 ) O(1) O(1) 次,所以复杂度是 O ( n 2 + m ) O(n^2+m) O(n2+m)

然后是记忆化搜素,显然不超过 O ( n m ) O(nm) O(nm) .

所以最后还大概是 O ( n 2 ) O(n^2) O(n2) 的级别,没有假掉。

但如果你最短路跑 Dijkstra 或 SPFA 甚至 Floyd,那可能就没了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

日居月诸Rijuyuezhu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值