Codeforces Round #130 (Div. 2) C - Police Station 最短路+dp
题目链接:
http://codeforces.com/problemset/problem/208/C
C. Police Station
memory limit per test:256 megabytes
题意
给你n个点(编号为1到n),m条边的有向图(无环,无重边,每个点都与其他点连通),现在要在一个点上建一个警察局,一条边,只要有一端与警察局相连,它就是安全的路,否则它就是不安全的路,现在问在哪个点建警察局能使从1到n的每条最短路上平均的安全路的条数(sigma(每条最短路上的安全路条数)/不同的最短路的条数)最多。
题解
首先跑一遍单源最短路求出所有最短路组成的DAG图g1,然后在DAG上dp两次,其中dp[i]表示从n到i的最短路条数,dp2[i]表示从1到i的最短路条数。
这样不同的最短路的条数就等于dp[1]或dp2[n];
由于只有100个点,所以我们暴力枚举在2到n-1哪个点设警察局(在起点和终点设的话,平均的安全路的条数刚好会等于1)。每个警察局只会影响到它的邻边,我们现在把邻边分为两类(在g1中),一类以x为终点的边,一类为以x为起点的边,枚举所有的边,对于第一类边(u,x),贡献值为dp2[u] * dp[x]。
对于第二类边(x,v),贡献值为dp2[x] * dp[v]。sigma(每条最短路上的安全路条数)=sigma(每条边的贡献值)。
所以最后的答案就是max(sigma_i(每条边的贡献值))/dp[1]。
(sigma_i表示第i条边设警察局时的sigma(每条边的贡献值),sigma表示求和符号)
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<queue>
using namespace std;
typedef long long LL;
const int maxn = 222;
int n, m;
vector<int> G[maxn];
//g1表示从1到n的所有最短路组成的DAG图。
vector<int> g1[maxn], g2[maxn];
int inq[maxn], d[maxn];
void spfa(int s) {
memset(d, 0x3f, sizeof(d));
queue<int> pq;
inq[s] = 0; d[s] = 0; pq.push(s);
while (!pq.empty()) {
int u = pq.front(); pq.pop();
inq[u] = 0;
for (int i = 0; i<G[u].size(); i++) {
int v = G[u][i];
if (d[v]>d[u] + 1) {
d[v] = d[u] + 1;
if (!inq[v]) inq[v] = 1, pq.push(v);
g2[v].clear();
g2[v].push_back(u);
}
else if (d[v] == d[u] + 1) {
g2[v].push_back(u);
}
}
}
}
void get_g1() {
for (int u = 1; u <= n; u++) {
// printf("%d: ",u);
for (int i = 0; i<g2[u].size(); i++) {
int v = g2[u][i];
// printf("%d ",v);
g1[v].push_back(u);
}
// puts("");
}
}
LL dp[maxn];//dp[i]表示从n到i之间的最短路数
LL dfs(int u) {
if (dp[u]) return dp[u];
for (int i = 0; i<g1[u].size(); i++) {
int v = g1[u][i];
dp[u] += dfs(v);
}
return dp[u];
}
LL dp2[maxn]; //dp2[i]表示从1到i之间的最短路数
LL dfs2(int u) {
if (dp2[u]) return dp2[u];
for (int i = 0; i<g2[u].size(); i++) {
int v = g2[u][i];
dp2[u] += dfs2(v);
}
return dp2[u];
}
LL solve() {
LL ret = dp[1];
for (int i = 2; i<n; i++) {
LL sum = 0;
for (int j = 0; j<g1[i].size(); j++) {
int v = g1[i][j];
sum += dp[v] * dp2[i];
}
for (int j = 0; j<g2[i].size(); j++) {
int v = g2[i][j];
sum += dp2[v] * dp[i];
}
ret = max(ret, sum);
}
return ret;
}
int main() {
scanf("%d%d", &n, &m);
while (m--) {
int u, v; scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
spfa(1);
get_g1();
memset(dp, 0, sizeof(dp));
dp[n] = 1;
LL fenmu = dfs(1);
memset(dp2, 0, sizeof(dp2));
dp2[1] = 1;
dfs2(n);
LL fenzi = solve();
printf("%.12lf\n", fenzi*1.0 / fenmu);
return 0;
}
乱七八糟
统计安全边的时候各种奇葩的想法,组合数学没学好,硬伤orz。