图论测试2 t1 幻想乡的异变 SPFA+最大流

11 篇文章 0 订阅
5 篇文章 0 订阅

第一题

考试的时候以为是费用流,于是花了二十几分钟兴致勃勃地打完光荣T掉。。。
后来评讲的时候发现是SPFA+最大流。
对于每个点,先求出1到这个点的最小距离,再求出这个点到n的最小距离,然后枚举每一条边,如果从1到这条边的起始点距离+这条边的边权+这条边的终点到n的距离==1到n的最短路的话,说明这条路就可能出现在1到n的最短路上,流量=1。
最后跑最大流就可以求出最多有多少条满足最短路的路径可以到达终点n。

关于如何求1到各个点与各个点到n的最短路:
从1到n的最短路就用SPFA,从各个点到n可以转化为从n到各个点,所以存反向边,再以n为起点跑一遍SPFA。但由于原图中边是有向的,所以反向边的存在可能会对最短路造成影响:
eg:从1到2有一条200的路,从2到1有一条15的路。
从1到2的最短路是200,但如果不判反向边,最短路就会变成15。所以每次更新时要判一下。

这道题的想法之前学长有讲过一道题很像,就是关于判断这条路在不在最短路上。感觉特别强2333。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define ms(x,y) memset(x,y,sizeof(x))
#define ll long long
#define INF 233333333
using namespace std;

const int N = 500010;

int n,m;
int num=1;
int head[N];

inline int Min(int a,int b){
    return a<b?a:b;
}

struct node{
    int pre,u,v,w,f;
}edge[N];

void addedge(int from,int to,int w){
    num++;
    edge[num].pre=head[from];
    edge[num].v=to;
    edge[num].u=from;
    edge[num].f=0;
    edge[num].w=w;
    head[from]=num;
}

deque<int> q;
ll dis[N][2];
bool vis[N];

void spfa(int s,int t,int x){
    q.push_back(s);dis[s][x]=0;vis[s]=true;
    while(!q.empty()){
        int u=q.front();q.pop_front();
        vis[u]=false;
        for(int i=head[u];i;i=edge[i].pre){
            int v=edge[i].v;
            if((i&1)==x&&dis[v][x]>dis[u][x]+edge[i].w){
                dis[v][x]=dis[u][x]+edge[i].w;
                if(!vis[v]){
                    vis[v]=true;
                    if(!q.empty()&&dis[v][x]<dis[q.front()][x])
                       q.push_front(v);
                    else q.push_back(v);
                }
            }
        }
    }
}

int dist[N];
queue<int> q1;
bool bfs(){
    vis[1]=true;q1.push(1);
    while(!q1.empty()){
        int u=q1.front();q1.pop();
        for(int i=head[u];i;i=edge[i].pre){
            int v=edge[i].v;
            if(!vis[v]&&edge[i].f){
                dist[v]=dist[u]+1;
                vis[v]=true;
                q1.push(v);
            }
        }
    }
    if(vis[n]) return true;
    return false;
}

int dfs(int u,int delta){
    if(u==n||delta==0) return delta;
    int ans=0;
    for(int i=head[u];i&&delta;i=edge[i].pre){
        int v=edge[i].v;
        if(dist[v]==dist[u]+1&&edge[i].f){
            int dd=dfs(v,Min(edge[i].f,delta));
            edge[i].f-=dd;
            edge[i^1].f+=dd;
            ans+=dd;
            delta-=dd;
        }
    }
    if(!ans) dist[u]=-1;
    return ans;
}

void zero(){
    ms(dist,0);ms(vis,0);
    while(!q1.empty()) q1.pop();
}

int maxflow(){
    int ans=0;
    while(1){
        zero();
        if(!bfs()) break;//此处有可能搜不到n。 
        ans+=dfs(1,INF);
    }
    return ans;
}

int main(){
    freopen("change.in","r",stdin);
    freopen("change.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        addedge(u,v,w);
        addedge(v,u,w);
    }
    ms(dis,127);ms(vis,0);
    spfa(1,n,0);
    ms(vis,0);while(!q.empty()) q.pop_front();
    spfa(n,1,1);
    for(int i=2;i<=num;i+=2){
        int u=edge[i].u,v=edge[i].v;
        if(dis[u][0]+edge[i].w+dis[v][1]==dis[n][0]){
            edge[i].f=1;
        }
    }
    printf("%d",maxflow());
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值