给定有向图,每条边只能用一次,求给定两点间有几条最短路。
看了看人家的文,有个很巧妙的方法,首先筛选出所有最短路,用这些在最短路上的边建图(每条边赋权值为一,保证只能使用一次),求一次给定点之间的最大流即可。
我用的 floyd 求最短路,dinic求最大流:
#include<cstdio>
#include<cstring>
#include<queue>
#define find_min(a,b) a<b?a:b
using namespace std;
const int N = 210;
const int MAX = 9999999999;
struct Edge{
int s,e,v,next;
}edge[5*N];
int e_num,head[N],d[N],sp,tp;
int n,dist[N][N],mat[N][N];
void AddEdge(int a,int b,int c){
edge[e_num].s=a; edge[e_num].e=b; edge[e_num].v=c;
edge[e_num].next=head[a]; head[a]=e_num++;
edge[e_num].s=b; edge[e_num].e=a; edge[e_num].v=0;
edge[e_num].next=head[b]; head[b]=e_num++;
}
int bfs(){
queue <int> q;
memset(d,-1,sizeof(d));
d[sp]=0;
q.push(sp);
while(!q.empty()){
int cur=q.front();
q.pop();
for(int i=head[cur];i!=-1;i=edge[i].next){
int u=edge[i].e;
if(d[u]==-1 && edge[i].v>0){//没有标记,且可行流大于0
d[u]=d[cur]+1;
q.push(u);
}
}
}
return d[tp] != -1;//汇点是否成功标号,也就是说是否找到增广路
}
int dfs(int a,int b){//a为起点
int r=0;
if(a==tp)return b;
for(int i=head[a];i!=-1 && r<b;i=edge[i].next){
int u=edge[i].e;
if(edge[i].v>0 && d[u]==d[a]+1){
int x=find_min(edge[i].v,b-r);
x=dfs(u,x);
r+=x;
edge[i].v-=x;
edge[i^1].v+=x;
}
}
if(!r)d[a]=-2;
return r;
}
void dinic(int sp,int tp){
int total=0,t;
while(bfs()){
while(t=dfs(sp,MAX))
total+=t;
}
printf("%d\n",total);
}
int main()
{
int i,j,k;
while(~scanf("%d",&n)){
for(i=0;i<n;i++){
for(j=0;j<n;j++){
scanf("%d",&mat[i][j]);
if(i==j)mat[i][j]=0;
if(mat[i][j]==-1)mat[i][j]=MAX;
dist[i][j]=mat[i][j];
}
}scanf("%d%d",&sp,&tp);
if(sp!=tp){
for(k=0;k<n;k++){
for(i=0;i<n;i++)
if(dist[i][k]<MAX)for(j=0;j<n;j++)
if(dist[k][j]<MAX)dist[i][j]=find_min(dist[i][j],dist[i][k]+dist[k][j]);
}
e_num=0;
memset(head,-1,sizeof(head));
for(i=0;i<n;i++){
if(dist[sp][i]<MAX)for(j=0;j<n;j++)
if(dist[j][tp]<MAX && mat[i][j]<MAX && dist[sp][i]+mat[i][j]+dist[j][tp]==dist[sp][tp])AddEdge(i,j,1);
}
dinic(sp,tp);
}
else puts("inf");
}
return 0;
}