hdu3416 Marriage Match IV(最短路+最大流)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3416

题目意思:有n个点,m条带权有向边,求点a到b有几条最短路(每条路只能走一次)。

先说一下写题过程,那叫一个惨啊!wa了20多遍,开始还没学网络流,以为就是最短路加搜索就可以,开始的思路是先求出各点到终点的最短路,再给路编号,搜索路,搜索到一条最短路就标记这条路走过的路全部编号,下次就不能搜索了,直到没有最短路为止。但最后错了。发现有一组反例:

 5  6

1 2 1

2 3 1

3 5 1

1 3 2

2 4 1

4 5 1

它找不要最优解,本来有两条,它只会找到1->2->3->5这一条。

看了一下题解说是最短路加最大流。那我就学了一下网络流,发现最终的思路是:先最短路求出图的最短路上的核心边,再用核心边建权为1的网络流图,最后求一遍最大流即可。

最短路核心边就是可以构成最短路的边。最短路的核心边才可能是要走的路,而设为权为1的网络流,可以从汇点的最大流看出从源点有多少流量流到汇点,即有几条路通过。

细节看代码:

/*
hdu3416 
题目:有n个点,m条带权有向边,求点a到b有几条最短路(每条路只能走一次)。
思路:先最短路求出图的最短路上的核心边,再用核心边建权为1的网络流图,最后求一遍最大流即可。 
*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=1100;
const int maxm=1e5+100;
struct node{
    int u,v,w,next;
}e[maxm],e2[2*maxm];
int head[maxm],head2[2*maxm],d[maxn];
int depth[2*maxm],visit[maxn];
int n,m,t,a,b,cnt,cnt2;
//e是原来的图,e2是建的网络流。 
void init(){
    memset(head,-1,sizeof(head));
    memset(head2,-1,sizeof(head2));
    memset(visit,0,sizeof(visit));
    memset(d,0x3f,sizeof(d));
    cnt=cnt2=0;
}

void add(int u,int v,int w){//原图连边 
    e[cnt].u=u;
    e[cnt].v=v;
    e[cnt].w=w;
    e[cnt].next=head[u];
    head[u]=cnt++;
}

void add2(int u,int v,int w){
    e2[cnt2].u=u;
    e2[cnt2].v=v;
    e2[cnt2].w=w;
    e2[cnt2].next=head2[u];
    head2[u]=cnt2++;
}

void spfa(){//求最短路 
    queue<int> q;
    q.push(a);
    visit[a]=1;
    d[a]=0;
    while(!q.empty()){
        int u=q.front();
        q.pop();
        visit[u]=0;
        for(int i=head[u];i!=-1;i=e[i].next){
            int v=e[i].v;
            int w=e[i].w;
            if(d[v]>d[u]+w){
                d[v]=d[u]+w;
                if(!visit[v]){
                    visit[v]=1;
                    q.push(v);
                }
            }
        }
    }
}

void creat_map(){//建网络流 
    for(int i=0;i<cnt;i++){
        int u=e[i].u;
        int v=e[i].v;
        int w=e[i].w;
        if((d[u]+w==d[v])&&(d[u]<inf&&d[v]<inf)){
            /*一定要判断这条路合法,即 d[u]<inf&&d[v]<inf,我在这卡了一组数据,测试样例:
             99
             3 2
             1 2 1
             1 2 2
             1 3
             */ 
            add2(u,v,1);//正向边建权为1 
            add2(v,u,0);//反向边建为0 
        }
    }
}

bool bfs(){//dinic分层 
    queue<int> q;
    memset(depth,0,sizeof(depth));
    q.push(a);
    depth[a]=1;
    while(!q.empty()){
        int u=q.front();
        q.pop();
        if(u==b)
            return true;
        for(int i=head2[u];i!=-1;i=e2[i].next){
            int v=e2[i].v;
            int w=e2[i].w;
            if(!depth[v]&&w){
                depth[v]=depth[u]+1;
                q.push(v);
            }
        }
    }
    return false;
}

int dfs(int u,int dis){//dinic的dfs搜索增广路 
    if(u==b)
        return dis;
    int res=0;
    for(int i=head2[u];i!=-1;i=e2[i].next){
        int v=e2[i].v;
        int w=e2[i].w;
        if((depth[v]==depth[u]+1)&&w){
            int di=dfs(v,min(w,dis-res));
            e2[i].w-=di;
            e2[i^1].w+=di;
            res+=di;
            if(res==dis)    
                return res;
        }
    }
    return res;
}

void dinic(){
    int ans=0;
    while(bfs()){
            ans+=dfs(a,inf);
    }
    printf("%d\n",ans);
}

int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        init();
        for(int i=0;i<m;i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w);
        }
        scanf("%d%d",&a,&b);
        spfa();
        creat_map();
        dinic();
    }
    return 0;
}

代码: 

猜你喜欢

转载自www.cnblogs.com/xiongtao/p/10313927.html
今日推荐