HDU 3416 Marriage Match IV (最短路径,网络流,最大流)
HDU 3416 Marriage Match IV (最短路径,网络流,最大流)
Description
Do not sincere non-interference。
Like that show, now starvae also take part in a show, but it take place between city A and B. Starvae is in city A and girls are in city B. Every time starvae can get to city B and make a data with a girl he likes. But there are two problems with it, one is starvae must get to B within least time, it's said that he must take a shortest path. Other is no road can be taken more than once. While the city starvae passed away can been taken more than once.
So, under a good RP, starvae may have many chances to get to city B. But he don't know how many chances at most he can make a data with the girl he likes . Could you help starvae?
Input
The first line is an integer T indicating the case number.(1<=T<=65)
For each case,there are two integer n and m in the first line ( 2<=n<=1000, 0<=m<=100000 ) ,n is the number of the city and m is the number of the roads.
Then follows m line ,each line have three integers a,b,c,(1<=a,b<=n,0<c<=1000)it means there is a road from a to b and it's distance is c, while there may have no road from b to a. There may have a road from a to a,but you can ignore it. If there are two roads from a to b, they are different.
At last is a line with two integer A and B(1<=A,B<=N,A!=B), means the number of city A and city B.
There may be some blank line between each case.
Output
Output a line with a integer, means the chances starvae can get at most.
Sample Input
3
7 8
1 2 1
1 3 1
2 4 1
3 4 1
4 5 1
4 6 1
5 7 1
6 7 1
1 7
6 7
1 2 1
2 3 1
1 3 3
3 4 1
3 5 1
4 6 1
5 6 1
1 6
2 2
1 2 1
1 2 2
1 2
Sample Output
2
1
1
Http
HDU:https://vjudge.net/problem/HDU-3416
Source
最短路径,网络流,最大流
题目大意
在给定的有向图中指定一个起点一个终点,求有几条边不重复的最短路径
解决思路
这道题刚开始看以为是一道费用流问题,但发现题目中要求走的只能是最短路径,所以可以知道最短路径是用来限制只能走一些边的,那么我们只要在求解最大流时(这里用Dinic实现)在bfs构造分层图时加入限制只能走最短路经过的边。
至于如何求出那些边是最短路边呢?我们可以正着跑一边最短路径(从起点到终点),再反着跑一边最短路径(从终点到起点,注意因为是有向边,所以边要反向)。注意,在网络流建图时其实是加入了反向边的,那么这里我们可以借助最小费用流的思想,把反边的费用置为-cost,那么我们在求解最短路径时就可以知道哪些是原图中的边而哪些是反边了。
如果我们将正着跑的最短路径放入Dist[1]中,反着跑的最短路径放入Dist[2]中,那么对于一条边u->v边权为w,若Dist[1][u]+w+Dist[2][v]==Dist[1][终点],则这条边是最短路边
然后我们把边的容量置为1(因为要满足没有重边),再对于这些限制条件跑最大流即可
另:这里使用Dinic实现最大流,可以参考这篇文章
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
const int maxN=1100;
const int maxM=103001*10;
const int inf=2147483647;
class Edge
{
public:
int u,v,cost,flow;//注意,这里虽然看起来和费用流的边构造是一样的,其实只是为了方便将最短路径的信息放进来了,实则无联系
};
int n,m;
int st,ed;//起点和终点
int cnt;
int Head[maxN];
int Next[maxM];
Edge E[maxM];
int Dist[3][maxN];//Dist[1]记录正着的最短路,Dist[2]记录反着的最短路
int Q[maxM];
bool inqueue[maxN];
int cur[maxN];//当前弧优化
int depth[maxN];
void Add_Edge(int u,int v,int cost,int flow);
void spfa1();//正着最短路
void spfa2();//反着最短路
bool bfs();
int dfs(int u,int flow);
int main()
{
int T;
scanf("%d",&T);
while (T--)
{
cnt=-1;
memset(Head,-1,sizeof(Head));//多组数据先置空
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
Add_Edge(u,v,w,1);//加边,注意因为一条边只能走一次,所以容量置为1
}
scanf("%d%d",&st,&ed);
spfa1();//正反分别最短路
spfa2();
/*
for (int i=1;i<=n;i++)
cout<<Dist[1][i]<<" ";
cout<<endl;
for (int i=1;i<=n;i++)
cout<<Dist[2][i]<<' ';
cout<<endl;
//*/
int Ans=0;//求解最大流
while (bfs())
{
for (int i=1;i<=n;i++)
cur[i]=Head[i];
while (dfs(st,inf))
Ans++;//注意这里每找到一条是+1,说明找到
}
printf("%d\n",Ans);
}
return 0;
}
void Add_Edge(int u,int v,int cost,int flow)
{
cnt++;
Next[cnt]=Head[u];
Head[u]=cnt;
E[cnt].u=u;
E[cnt].v=v;
E[cnt].cost=cost;
E[cnt].flow=flow;
cnt++;//反边,注意与正边的区别
Next[cnt]=Head[v];
Head[v]=cnt;
E[cnt].u=v;
E[cnt].v=u;
E[cnt].cost=-cost;
E[cnt].flow=0;
return;
}
void spfa1()//正着最短路
{
memset(Dist[1],127,sizeof(Dist[1]));
memset(inqueue,0,sizeof(inqueue));
int h=1,t=0;
Q[1]=st;//起点是st
Dist[1][st]=0;
inqueue[st]=1;
do
{
t++;
int u=Q[t];
for (int i=Head[u];i!=-1;i=Next[i])
{
int v=E[i].v;
if ((E[i].flow>0)&&(E[i].cost+Dist[1][u]<Dist[1][v]))//注意这里利用了初始时正边的流量>0的性质来判断是否是正边
{
Dist[1][v]=Dist[1][u]+E[i].cost;
if (inqueue[v]==0)
{
h++;
Q[h]=v;
inqueue[v]=1;
}
}
}
inqueue[u]=0;
}
while (t!=h);
return;
}
void spfa2()//反着最短路
{
memset(Dist[2],127,sizeof(Dist[2]));
memset(inqueue,0,sizeof(inqueue));
int h=1,t=0;
Q[1]=ed;//起点是ed
inqueue[ed]=1;
Dist[2][ed]=0;
do
{
t++;
int u=Q[t];
for (int i=Head[u];i!=-1;i=Next[i])
{
int v=E[i].v;
if ((E[i].cost<0)&&(Dist[2][u]-E[i].cost<Dist[2][v]))//因为反边的边权是负数,所以可以用来标记是否是反边,同时注意松弛时是加上其相反数
{
Dist[2][v]=Dist[2][u]-E[i].cost;
if(inqueue[v]==0)
{
h++;
Q[h]=v;
inqueue[v]=1;
}
}
}
inqueue[u]=0;
}
while (t!=h);
return;
}
bool bfs()//构造分层图
{
memset(depth,-1,sizeof(depth));
int h=1,t=0;
Q[1]=st;
depth[st]=1;
do
{
t++;
int u=Q[t];
for (int i=Head[u];i!=-1;i=Next[i])
{
int v=E[i].v;
if ((E[i].flow>0)&&(depth[v]==-1)&&(Dist[1][u]+E[i].cost+Dist[2][v]==Dist[1][ed]))//注意这里与普通的Dinic多加了一个条件,即这条边必须是最短路边
{
depth[v]=depth[u]+1;
h++;
Q[h]=v;
}
}
}
while (t!=h);
if (depth[ed]==-1)
return 0;
return 1;
}
int dfs(int u,int flow)
{
if (u==ed)
return flow;
for (int &i=cur[u];i!=-1;i=Next[i])
{
int v=E[i].v;
if ((depth[v]==depth[u]+1)&&(E[i].flow>0)&&(Dist[1][u]+E[i].cost+Dist[2][v]==Dist[1][ed]))//这里的判断也多了一个条件,同样是判断是否是最短路边
{
int di=dfs(v,min(flow,E[i].flow));
if (di>0)
{
E[i].flow-=di;
E[i^1].flow+=di;
return di;
}
}
}
return 0;
}