codeforces 208C 图的拓扑排序+简单DP

链接http://www.codeforces.com/problemset/problem/208/C

题意:给你一幅图,每条边边权为1,要求你在某个位置建一个警察局,使得从1到n的所有最短路中与这个警察局有接触的边的条数最多

ps:

假如一条路径经过警察局,那么肯定会经过两条与警察局有关的边(起点、终点除外)

解法:

先对图求点对间的最短路,处理出新图,在新图中,所有从s到t的路径都是最短路径,然后利用拓扑排序得到的拓扑序(也可以广搜)处理出s到某个点共有几条路(简单DP)。

(在反图中)反过来再重复一遍

in[i]表示从s到i的总路径数,out[i]表示从t到i的总路径数

那么in[i]*out[i]就是经过i的总路径数

#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
void Max(double &a,double b){if(b>a) a=b;}
const int inf = ~0u>>2;
int mp[105][105];
int n,m;
struct {
    int a,b;
}edge[10010];
vector<int> Edge[101],fedge[101];
int c[110];
int topo[110];
int T;
bool dfs(int u){
    c[u]=-1;
    for(int i=0;i<Edge[u].size();i++){
        int v=Edge[u][i];
        if(c[v]<0) return false;
        else if(!c[v] && !dfs(v)) return false;
    }
    c[u]=1;
    topo[--T]=u;
    return true;
}
void topsort(int n){
    T=n+1;
    memset(c,0,sizeof(c));
    dfs(1);
}
double in[110],out[110];
int main(){
    while(scanf("%d%d",&n,&m)!=EOF){
        for(int i=0;i<=n;i++) Edge[i].clear();
        fill(mp[0],mp[101],inf);
        for(int i=1;i<=n;i++) mp[i][i]=0;
        int a,b;
        for(int i=1;i<=m;i++){
            scanf("%d%d",&a,&b);
            edge[i].a=a;
            edge[i].b=b;
            mp[a][b]=mp[b][a]=1;
        }
        for(int k=1;k<=n;k++)for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
        if(mp[i][k]+mp[k][j]<mp[i][j]) mp[i][j]=mp[i][k]+mp[k][j];
        memset(in,0,sizeof(in));
        memset(out,0,sizeof(out));
        for(int i=1;i<=m;i++){
            int a=edge[i].a,b=edge[i].b;
            if(mp[1][a]+mp[a][b]+mp[b][n]==mp[1][n]){
                Edge[a].push_back(b);
                fedge[b].push_back(a);
            }
            if(mp[1][b]+mp[b][a]+mp[a][n]==mp[1][n]){
                Edge[b].push_back(a);
                fedge[a].push_back(b);
            }
        }
        topsort(n);
        for(int i=T;i<=n;i++){
            int s=topo[i];
            if(!in[s]) in[s]=1;
            for(int j=0;j<Edge[s].size();j++){
                int t=Edge[s][j];
                in[t]+=in[s];
            }
        }
        for(int i=n;i>=T;i--){
            int s=topo[i];
            if(!out[s]) out[s]=1;
            for(int j=0;j<fedge[s].size();j++){
                int t=fedge[s][j];
                out[t]+=out[s];
            }
        }
        double t=in[n];
        double ans=0;
        for(int i=1;i<=n;i++) {
            double tmp=(in[i]*out[i]);
            if(i!=1 && i!=n) tmp*=2;
            Max(ans,tmp/t);
        }
        printf("%lf\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值