http://acm.hdu.edu.cn/showproblem.php?pid=6582

思路:找到最短路核心边建图,跑一遍最小割,最短路核心边的定义为设起点到每个点的最短距离为d[i],每个点到终点的最短路为d2[i],如果一条边起点为u,终点为v,边权为w,若d[u]+d2[v]+w==d[n]则这是一条最短路核心边。所以先用spfa求d[i],然后反向spfa求d2[i],最后建图dinic求出答案。

#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<cstdio>
#include<stack>
#include<cmath>
#include<iostream>
#define ll long long
#define lowbit(x) x&(-x)
using namespace std;
const int maxn=20100;
const int maxm=20100;
const ll inf=1e18;
struct node{
    int u,v,nxt;
    ll w;
}e[2*maxm],e2[2*maxm],e3[2*maxm];
int h[maxn],h2[maxn],h3[maxn],depth[maxn];
ll d[maxn],d2[maxn];
bool vis[maxn];
int n,m,st,ed,cnt,cnt2,cnt3;
void init()
{
    memset(h,-1,sizeof(h));
    memset(h2,-1,sizeof(h2));
    memset(h3,-1,sizeof(h3));
    for(int i=0;i<=n;i++)
        d[i]=d2[i]=inf;
    cnt=cnt2=cnt3=0;
}

void add(int u,int v,ll w)//正向建边 
{
    e[cnt].u=u,e[cnt].v=v,e[cnt].w=w;
    e[cnt].nxt=h[u];h[u]=cnt++;
}

void add2(int u,int v,ll w)//反向建边 
{
    e2[cnt2].u=u,e2[cnt2].v=v,e2[cnt2].w=w;
    e2[cnt2].nxt=h2[u],h2[u]=cnt2++;
}
void add3(int u,int v,ll w)//重新建边 
{
    e3[cnt3].u=u,e3[cnt3].v=v,e3[cnt3].w=w;
    e3[cnt3].nxt=h3[u],h3[u]=cnt3++;
}

bool spfa()//求每点到1的最短距离 
{
    queue<int> q;
    memset(vis,0,sizeof(vis));
    d[st]=0;
    vis[st]=1;
    q.push(st);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        vis[u]=0;
        for(int i=h[u];i!=-1;i=e[i].nxt)
        {
            int v=e[i].v;
            ll  w=e[i].w;
            if(d[v]>d[u]+w)
            {
                d[v]=d[u]+w;
                if(!vis[v])
                {
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
    return d[n]==inf;
}

void re_spfa()//求每点到n的最短距离 
{
    queue<int> q;
    memset(vis,0,sizeof(vis));
    d2[ed]=0;
    vis[ed]=1;
    q.push(ed);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        vis[u]=0;
        for(int i=h2[u];i!=-1;i=e2[i].nxt)
        {
            int v=e2[i].v;
            ll  w=e2[i].w;
            if(d2[v]>d2[u]+w)
            {
                d2[v]=d2[u]+w;
                if(!vis[v])
                {
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
}
  
void create_map()//重新建边 
{
    for(int i=0;i<cnt;i++)
    {
        int u=e[i].u;
        int v=e[i].v;
        ll  w=e[i].w;
        if((d[u]+d2[v]+w==d[ed])&&(d[u]<inf&&d2[v]<inf))
        {//最短路核心边 
            add3(u,v,w);
            add3(v,u,0); 
        }
    }
}

bool bfs(){//dinic分层 
    queue<int> que;
    memset(depth,0,sizeof(depth));
    que.push(st);
    depth[st]=1;
    while(!que.empty()){
        int u=que.front();
        que.pop();
        if(u==ed)
            return true;
        for(int i=h3[u];i!=-1;i=e3[i].nxt){
            int v=e3[i].v;
            ll  w=e3[i].w;
            if(!depth[v]&&w){
                depth[v]=depth[u]+1;
                que.push(v);
            }
        }
    }
    return false;
}

ll dfs(int u,ll dis)
{
    if(u==ed)
        return dis;
    ll res=0;
    for(int i=h3[u];i!=-1;i=e3[i].nxt)
    {
        int v=e3[i].v;
        ll  w=e3[i].w;
        if((depth[v]==depth[u]+1)&&w)
        {
            ll di=dfs(v,min(w,dis-res));
            e3[i].w-=di;
            e3[i^1].w+=di;
            res+=di;
            if(res==dis)
                return dis;
        }
    }
    return res;
}

void dinic()//dinic求最小割 
{
    ll ans=0;
    while(bfs())
    {
        ans+=dfs(st,inf);
    }
    printf("%lld\n",ans);
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        st=1,ed=n;
        init();
        for(int i=0;i<m;i++)
        {
            int u,v;ll w;
            scanf("%d%d%lld",&u,&v,&w);
            add(u,v,w);
            add2(v,u,w);
        }
        if(spfa())//特判,没有最短路 
            printf("0\n");
        else
        {
            re_spfa();
            create_map();
            dinic();
        }    
    }
    return 0;
}