题意
有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;
}