题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3416
题目意思:有n个点,m条带权有向边,求点a到b有几条最短路(每条路只能走一次)。
先说一下写题过程,那叫一个惨啊!wa了20多遍,开始还没学网络流,以为就是最短路加搜索就可以,开始的思路是先求出各点到终点的最短路,再给路编号,搜索路,搜索到一条最短路就标记这条路走过的路全部编号,下次就不能搜索了,直到没有最短路为止。但最后错了。发现有一组反例:
5 6
1 2 1
2 3 1
3 5 1
1 3 2
2 4 1
4 5 1
它找不要最优解,本来有两条,它只会找到1->2->3->5这一条。
看了一下题解说是最短路加最大流。那我就学了一下网络流,发现最终的思路是:先最短路求出图的最短路上的核心边,再用核心边建权为1的网络流图,最后求一遍最大流即可。
最短路核心边就是可以构成最短路的边。最短路的核心边才可能是要走的路,而设为权为1的网络流,可以从汇点的最大流看出从源点有多少流量流到汇点,即有几条路通过。
细节看代码:
/* hdu3416 题目:有n个点,m条带权有向边,求点a到b有几条最短路(每条路只能走一次)。 思路:先最短路求出图的最短路上的核心边,再用核心边建权为1的网络流图,最后求一遍最大流即可。 */ #include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<queue> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; const int maxn=1100; const int maxm=1e5+100; struct node{ int u,v,w,next; }e[maxm],e2[2*maxm]; int head[maxm],head2[2*maxm],d[maxn]; int depth[2*maxm],visit[maxn]; int n,m,t,a,b,cnt,cnt2; //e是原来的图,e2是建的网络流。 void init(){ memset(head,-1,sizeof(head)); memset(head2,-1,sizeof(head2)); memset(visit,0,sizeof(visit)); memset(d,0x3f,sizeof(d)); cnt=cnt2=0; } void add(int u,int v,int w){//原图连边 e[cnt].u=u; e[cnt].v=v; e[cnt].w=w; e[cnt].next=head[u]; head[u]=cnt++; } void add2(int u,int v,int w){ e2[cnt2].u=u; e2[cnt2].v=v; e2[cnt2].w=w; e2[cnt2].next=head2[u]; head2[u]=cnt2++; } void spfa(){//求最短路 queue<int> q; q.push(a); visit[a]=1; d[a]=0; while(!q.empty()){ int u=q.front(); q.pop(); visit[u]=0; for(int i=head[u];i!=-1;i=e[i].next){ int v=e[i].v; int w=e[i].w; if(d[v]>d[u]+w){ d[v]=d[u]+w; if(!visit[v]){ visit[v]=1; q.push(v); } } } } } void creat_map(){//建网络流 for(int i=0;i<cnt;i++){ int u=e[i].u; int v=e[i].v; int w=e[i].w; if((d[u]+w==d[v])&&(d[u]<inf&&d[v]<inf)){ /*一定要判断这条路合法,即 d[u]<inf&&d[v]<inf,我在这卡了一组数据,测试样例: 99 3 2 1 2 1 1 2 2 1 3 */ add2(u,v,1);//正向边建权为1 add2(v,u,0);//反向边建为0 } } } bool bfs(){//dinic分层 queue<int> q; memset(depth,0,sizeof(depth)); q.push(a); depth[a]=1; while(!q.empty()){ int u=q.front(); q.pop(); if(u==b) return true; for(int i=head2[u];i!=-1;i=e2[i].next){ int v=e2[i].v; int w=e2[i].w; if(!depth[v]&&w){ depth[v]=depth[u]+1; q.push(v); } } } return false; } int dfs(int u,int dis){//dinic的dfs搜索增广路 if(u==b) return dis; int res=0; for(int i=head2[u];i!=-1;i=e2[i].next){ int v=e2[i].v; int w=e2[i].w; if((depth[v]==depth[u]+1)&&w){ int di=dfs(v,min(w,dis-res)); e2[i].w-=di; e2[i^1].w+=di; res+=di; if(res==dis) return res; } } return res; } void dinic(){ int ans=0; while(bfs()){ ans+=dfs(a,inf); } printf("%d ",ans); } int main(){ scanf("%d",&t); while(t--){ scanf("%d%d",&n,&m); init(); for(int i=0;i<m;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); add(u,v,w); } scanf("%d%d",&a,&b); spfa(); creat_map(); dinic(); } return 0; }