给定有向图,每条边只能用一次,求给定两点间有几条最短路。
看了看人家的文,有个很巧妙的方法,首先筛选出所有最短路,用这些在最短路上的边建图(每条边赋权值为一,保证只能使用一次),求一次给定点之间的最大流即可。
我用的 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; }