题意
有N(1<=N<=5000)个点,m条边(1<=M<=50000)。起点可以是任何一个入度为0的点,终点是N。求从起点到终点的所有路中,经过次数最大的一条路。输出经过次数。(规定每个点需要连接到编号更大的点,且不存在循环)
题解
该图为DAG(有向无环图),可利用拓扑排序,正反向建图分别进行两次拓扑排序。
根据乘法原理,在一条边M(u->v)上,通过边M的次数为从源点到达u的方式数量×从终点到达v的方式数量。
Code
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; struct node{ int to,next; }edge1[50005],edge2[50005]; int n,m; int cnt1,head1[5005],indegree1[5005],num1[5005]; int cnt2,head2[5005],indegree2[5005],num2[5005]; void add(int u,int v) { /*正向建图*/ edge1[cnt1].to=v; edge1[cnt1].next=head1[u]; head1[u]=cnt1++; /*反向建图*/ edge2[cnt2].to=u; edge2[cnt2].next=head2[v]; head2[v]=cnt2++; } void topoSort1()//对正向图进行拓扑排序 { queue<int>q; for(int i=1;i<=n;i++){ if(!indegree1[i]){ q.push(i);//入度为0的点入队 num1[i]=1; } } while(!q.empty()){ int x=q.front();q.pop(); for(int j=head1[x];~j;j=edge1[j].next){ indegree1[edge1[j].to]--; num1[edge1[j].to]+=num1[x];//记录该点的正向总入度 if(!indegree1[edge1[j].to]) q.push(edge1[j].to); } } } void topoSort2()//对反向图进行拓扑排序 { queue<int>q; for(int i=1;i<=n;i++){ if(!indegree2[i]){ q.push(i); num2[i]=1; } } while(!q.empty()){ int x=q.front();q.pop(); for(int j=head2[x];~j;j=edge2[j].next){ indegree2[edge2[j].to]--; num2[edge2[j].to]+=num2[x];//记录该点的反向总入度 if(!indegree2[edge2[j].to]) q.push(edge2[j].to); } } } int main() { int u,v; while(~scanf("%d%d",&n,&m)){ cnt1=0,cnt2=0; memset(indegree1,0,sizeof(indegree1)); memset(indegree2,0,sizeof(indegree2)); memset(head1,-1,sizeof(head1)); memset(head2,-1,sizeof(head2)); memset(num1,0,sizeof(num1)); memset(num2,0,sizeof(num2)); for(int i=1;i<=m;i++){ scanf("%d%d",&u,&v); indegree1[v]++;//记录正向图各点的入度 indegree2[u]++;//记录反向图各点的入度 add(u,v); } topoSort1(); topoSort2(); int ans=0; for(int i=1;i<=n;i++){ for(int j=head1[i];~j;j=edge1[j].next){ //枚举每一条边 ans=max(ans,num1[i]*num2[edge1[j].to]);//(u->v)u点正向总入度和v点反向总入度的乘积,更新答案最大值 } } printf("%d\n",ans); } return 0; }