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;
}


区间DP是一种动态规划的方法,用于解决区间范围内的问题。在Codeforces竞赛中,区间DP经常被用于解决一些复杂的字符串或序列相关的问题。 在区间DP中,dp[i][j]表示第一个序列前i个元素和第二个序列前j个元素的最优解。具体的转移方程会根据具体的问题而变化,但是通常会涉及到比较两个序列的元素是否相等,然后根据不同的情况进行状态转移。 对于区间长度为1的情况,可以先进行初始化,然后再通过枚举区间长度和区间左端点,计算出dp[i][j]的值。 以下是一个示例代码,展示了如何使用区间DP来解决一个字符串匹配的问题: #include <cstdio> #include <cstring> #include <string> #include <iostream> #include <algorithm> using namespace std; const int maxn=510; const int inf=0x3f3f3f3f; int n,dp[maxn][maxn]; char s[maxn]; int main() { scanf("%d", &n); scanf("%s", s + 1); for(int i = 1; i <= n; i++) dp[i][i] = 1; for(int i = 1; i <= n; i++) { if(s[i] == s[i - 1]) dp[i][i - 1] = 1; else dp[i][i - 1] = 2; } for(int len = 3; len <= n; len++) { int r; for(int l = 1; l + len - 1 <= n; l++) { r = l + len - 1; dp[l][r] = inf; if(s[l] == s[r]) dp[l][r] = min(dp[l + 1][r], dp[l][r - 1]); else { for(int k = l; k <= r; k++) { dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r]); } } } } printf("%d\n", dp[n]); return 0; } 希望这个例子能帮助你理解区间DP的基本思想和应用方法。如果你还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值