HDU 3416
//题目大意:求两点之间最短路的条数,但是前提是已走过的路径不能重复
//题目类型:最大流+最短路径
//解题思路:首先用Dijkstra求出两点之间的最短路,然后判断每条边是否在最短路上,如果在最短路上,则流量自增1 ,然后求出最大流
#include <iostream>
#include <algorithm>
#include <queue>
#include <cstdio>
//#include <conio.h>
using namespace std;
const int narray = 1010;
const int marray = 100010;
const int INF=0x7fffffff;
struct edge_T
{
int u; //起点(为了最大流建图方便)
int v; //终点
int w;
edge_T *next;
}*adj[narray], edges[marray],*readj[narray],reedges[marray];
bool final[marray];
struct node_T
{
int v;
int len;
bool operator < (const node_T &nod) const
{
return len > nod.len;
}
};
int edgenum,reedgenum; //记录边的总数
int vn,en; //记录定点数和边数
int src,des;
int dista[narray],distb[narray];
void addEdge(int u, int v, int w)
{
edge_T *ptr = &edges[edgenum];
ptr->u = u;
ptr->v = v;
ptr->w = w;
ptr->next = adj[u]; //往前插入边
adj[u] = ptr;
edgenum++;
}
void addReedge(int u,int v,int w)
{
edge_T *ptr = &reedges[reedgenum];
ptr->u = u;
ptr->v = v;
ptr->w = w;
ptr->next = readj[u]; //往前插入边
readj[u] = ptr;
reedgenum++;
}
int dijkstra(int s,int t,int dist[],int flag)
{
int i;
priority_queue <node_T> Q; //使用优先级队列实现
node_T cur;
edge_T *ptr;
cur.v = s;
cur.len = 0;
Q.push(cur);
for(int i = 1; i <= vn; i ++)
{
dist[i] = INF;
}
dist[s] = 0;
while(!Q.empty())
{
int v = Q.top().v; //优先级队列使用top()
int len = Q.top().len;
Q.pop();
if(dist[v]!=len) continue;
if(flag == 1) ptr = adj[v];
else ptr = readj[v];
for(; ptr; ptr = ptr -> next)
{
int u = ptr -> v;
int w = ptr -> w;
if(dist[v] + w < dist[u])
{
dist[u] = dist[v] + w;
cur.v = u;
cur.len = dist[u];
Q.push(cur);
}
}
}
return dist[t];
}
int capacity[narray][narray];
int pre[narray];
int num[narray];
int d[narray];
void init(int src,int des)
{
int i,j;
queue<int> myqueue;
myqueue.push(des);
memset(num,0,sizeof(num));
for(i=1;i<=vn;++i)
d[i] = INF;
d[des] = 0;
num[0] = 1;
int frontint;
while(!myqueue.empty())
{
frontint = myqueue.front();myqueue.pop();
for(i=1;i<=vn;++i)
{
if(d[i]>=vn && capacity[i][frontint]>0)
{
d[i] = d[frontint]+1;
myqueue.push(i);
num[d[i]]++;
}
}
}
}
int findarc(int t)
{
int i,j;
for(i=1;i<=vn;++i)
{
if(capacity[t][i]>0 && d[t]==d[i]+1) return i;
}
return -1;
}
int relabel(int t)
{
int i,j;
int mm = INF;
for(i=1;i<=vn;++i)
{
if(capacity[t][i]>0) mm = min(mm,d[i]+1);
}
return mm==INF?vn:mm;
}
int maxFlow(int src,int des)
{
int sumflow = 0;
int delta;
int i=src;
int j;
memset(pre,-1,sizeof(pre));
while(d[src]<vn)
{
j = findarc(i);
if(j>=0)
{
pre[j] = i;
i = j;
if(i==des)
{
delta = INF;
for (i=des;i!=src;i=pre[i]) delta=min(delta,capacity[pre[i]][i]);
for (i=des;i!=src;i=pre[i]) capacity[pre[i]][i] -= delta, capacity[i][pre[i]] += delta;
sumflow += delta;
}
}
else
{
int x = relabel(i);
num[x]++;
num[d[i]]--;
if(num[d[i]]==0) return sumflow;
d[i] = x;
if(i!=src) i =pre[i];
}
}
return sumflow;
}
//见图并求最大流
void buildAndMaxFlow(int res)
{
int i,j;
memset(capacity,0,sizeof(capacity));
for(i=1;i<edgenum;++i) //遍历所有的边(此处使用edgenum,因为可能存在负权回路的情况)
{
int u = edges[i].u;
int v = edges[i].v;
int w = edges[i].w;
if(dista[u]+distb[v]+w==res) //如果边的起点到源点的距离+边的权值+边的终点到汇点的距离等于汇点到源点的距离
{
capacity[u][v]++;
}
}
init(src,des); //在求最大流之前首先初始化标顶
printf("%d\n",maxFlow(src,des));
}
int main()
{
//freopen("1.txt","r",stdin);
int t;
int i;
int res;
scanf("%d",&t);
while(t--)
{
scanf("%d %d", &vn, &en);
for(i = 1; i <= vn; i ++)
{
adj[i] = NULL; //初始化邻接表的指针
readj[i] = NULL;
}
edgenum = 1; //初始化边数
reedgenum =1;
int u, v;
int w;
for(i = 1; i <= en; i ++)
{
scanf("%d%d%d", &u, &v, &w);
if(u==v) continue; //判断是否有环
addEdge(u, v, w);
addReedge(v,u,w);
}
scanf("%d%d",&src,&des);
//分别求出途中各点到源点和终点之间的距离
dijkstra(src,des,dista,1);
res = dijkstra(des,src,distb,2);
buildAndMaxFlow(res);
}
//getch();
return 0;
}