給定有向圖,每條邊只能用一次,求給定兩點間有幾條最短路。
看了看人家的文,有個很巧妙的方法,首先篩選出所有最短路,用這些在最短路上的邊建圖(每條邊賦權值為一,保證只能使用一次),求一次給定點之間的最大流即可。
我用的 floyd 求最短路,dinic求最大流:
#include<cstdio>#include<cstring>#include<queue>#define find_min(a,b) a<b?a:busing 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){//沒有標記,且可行流大於0d[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; }