Time Limit: 1000MS
Memory Limit: 65536KB
Problem Description
一个无环的有向图称为无环图(Directed Acyclic Graph),简称DAG图。
AOE(Activity On Edge)网:顾名思义,用边表示活动的网,当然它也是DAG。与AOV不同,活动都表示在了边上,如下图所示:
如上所示,共有11项活动(11条边),9个事件(9个顶点)。整个工程只有一个开始点和一个完成点。即只有一个入度为零的点(源点)和只有一个出度为零的点(汇点)。
关键路径:是从开始点到完成点的最长路径的长度。路径的长度是边上活动耗费的时间。如上图所示,1 到2 到 5到7到9是关键路径(关键路径不止一条,请输出字典序最小的),权值的和为18。
Input
这里有多组数据,保证不超过10组,保证只有一个源点和汇点。输入一个顶点数n(2<=n<=10000),边数m(1<=m <=50000),接下来m行,输入起点sv,终点ev,权值w(1<=sv,ev<=n,sv != ev,1<=w <=20)。数据保证图连通。
Output
关键路径的权值和,并且从源点输出关键路径上的路径(如果有多条,请输出字典序最小的)。
Example Input
9 11 1 2 6 1 3 4 1 4 5 2 5 1 3 5 1 4 6 2 5 7 9 5 8 7 6 8 4 8 9 4 7 9 2
Example Output
18 1 2 2 5 5 7 7 9
Hint
Author
这道题目就是求有向图的关键路径也可以说是 有向图两点之间的最长路径(SpFa算法) 而且题目数据还是非常仁慈的源点和汇点默认就是 1和n 否则只能多用两个数组来判断了
/*******方法一 求最长路径*********/
#include <bits/stdc++.h>
using namespace std;
const int MAX = 50000; //题目约定的最大值
class Edge // 有向图的边(适用于邻接表 每个定点可以用vector来存放 这里用的是数组存放)
{
public:
int x,y,data;
int next;
Edge(int x = -1,int y =-1,int data = -1,int next = -1)
{
this->x = x;
this->y = y;
this->data = data;
this->next = next;
}
};
int main()
{
int n,M;
while(~scanf("%d%d",&n,&M))
{
int head[MAX];/// 存放每个邻接表的顶点
Edge m[MAX];/// 边的数目
int cnt = 0 ;
int per[MAX];/// 存放每条边的前驱
int ans[MAX];/// 存放源点到各个目标点的最长路径
for(int i = 0 ; i<=n; i++)///初始化
{
ans[i] = -INT_MAX;
per[i] = -1;
head[i] = -1 ;
}
ans[n] = 0 ;
for(int i = 0 ; i<M; i++)///创建邻接表
{
int x,y,data;
scanf("%d%d%d",&y,&x,&data);/// (反向建立图方便最后输出最长路径)
Edge t(x,y,data,-1);///生成条
m[cnt] = t;
m[cnt].next = head[x];
head[x] = cnt++;
}
queue<int>l;
l.push(n);
while(!l.empty())///SPFA 算法
{
int t = l.front();
l.pop() ;
for(t = head[t]; t!= -1 ; t = m[t].next)
{
int from = m[t].x;
int to = m[t].y;
int data = m[t].data;
if(ans[to] < data + ans[from] || (ans[to] == data + ans[from] && per[to] > from)) /// 加入这条边的条件是加入这条边后最长度变大 或者 路径长度减小
{
ans[to] = data + ans[from];
per[to] = from;
l.push(to) ;
}
}
}
printf("%d\n",ans[1]) ;
for(int i =1; per[i] != -1; i = per[i])///输出路径
{
printf("%d %d\n",i,per[i]) ;
}
}
return 0 ;
}
/***************************************************
User name: 大铁棍子医院铜主任
Result: Accepted
Take time: 48ms
Take Memory: 1128KB
Submit time: 2017-11-28 21:03:42
****************************************************/
/** 和上面是同一个算法不过就是封装在一起了,不过是RUNAnswer 希望有大神可以帮助一下***/
#include <bits/stdc++.h>
using namespace std;
const int MAX = 50000; //题目约定的最大值
class Edge // 有向图的边(适用于邻接表 每个定点可以用vector来存放 这里用的是数组存放)
{
public:
int x,y,data;
int next;
Edge(int x = -1,int y =-1,int data = -1,int next = -1)
{
this->x = x;
this->y = y;
this->data = data;
this->next = next;
}
};
class Map
{
public:
vector<int>head[MAX] ;
vector<Edge>m;
Map()
{
for(int i = 0 ; i<MAX; i++)
{
head[i].clear() ;
}
m.clear() ;
}
void Add_Edge(int x,int y, int data)
{
m.push_back(Edge(x,y,data));
int len = m.size();
head[x].push_back(len-1) ;
}
void spfa(int x ,int y)
{
int per[MAX];/// 存放每条边的前驱
int ans[MAX];/// 存放源点到各个目标点的最长路径
for(int i= 0 ;i<=MAX;i++)
{
per[i] = -1 ;
ans[i] = -INT_MAX;
}
ans[x] = 0 ;
queue<int>l;
l.push(x);
while(!l.empty())///SPFA 算法
{
int t = l.front();
l.pop() ;
for(int i =0;i<head[t].size() ;i++)
{
Edge e = m[head[t][i]];
int from = e.x;
int to = e.y;
int data = e.data;
if(ans[to] < data + ans[from] || (ans[to] == data + ans[from] && per[to] > from)) /// 加入这条边的条件是加入这条边后最长度变大 或者 路径长度减小
{
ans[to] = data + ans[from];
per[to] = from;
l.push(to) ;
}
}
}
printf("%d\n",ans[1]) ;
for(int i =1; per[i] != -1; i = per[i])///输出路径
{
printf("%d %d\n",i,per[i]) ;
}
}
};
int main()
{
int n,M;
while(~scanf("%d%d",&n,&M))
{
Map a;
for(int i = 0 ; i<M; i++)///创建邻接表
{
int x,y,data;
scanf("%d%d%d",&x,&y,&data);
a.Add_Edge(y,x,data);
}
a.spfa(n,1) ;
}
return 0 ;
}
下面是正规的关键路径算法,比较复杂不建议使用
/** 算法分析,先用拓扑排序判断是否有回路,然后在算出每个活动的最早开始时间
*** 最晚开始时间然后在算出关键路径***/
#include <bits/stdc++.h>
using namespace std;
class Edge
{
public:
int from,to, data;
Edge(int x=0,int y=0,int z=0) : from(x), to(y), data(z) {}
};
class Vertex
{
public:
int id;
int ans; /// 节点入度
vector<int >next;
};
class Map
{
private:
int n;
bool visit[10000+10] ;
vector<Edge> E;
vector<int> p; /// 存放拓扑排序序列
int ve[10000+10];/// 顶点活动发生的最早发生时间
int vl[10000+10];/// 定点活动发生的最晚时间
public:
Vertex G[10000+10];
void Init(int n)
{
this->n = n;
E.clear();
p.clear();
for(int i = 0 ; i<= n; i++)
{
ve[i] = - 1 ;
vl[i] = INT_MAX;
visit[i] = false;
G[i].ans = 0 ;
G[i].next.clear();
G[i].id = i;
}
}
void Add_Edge(int x,int y,int z=0)
{
E.push_back(Edge(x,y,z));
int len = E.size();
G[x].next.push_back(len - 1 ) ;
G[y].ans++;
}
bool TopSort()
{
queue<int> L;
for(int i = 1; i<=n ; i++) /// 将入度为零的点进栈
{
if(G[i].ans == 0)
{
L.push(G[i].id);
}
}
while(!L.empty())
{
int t = L.front();
L.pop();
p.push_back(t);
visit[t] = true;
int len = G[t].next.size();
for(int i = 0 ; i<len; i++ ) /// 删边
{
int j =E[G[t].next[i]].to;
int data = E[G[t].next[i]].data;
if(visit[j] == false)
{
G[j].ans--;
if(G[j].ans == 0)
{
L.push(j) ;
}
}
}
}
if(p.size() == n)
{
return true;
}
return false;
}
void BFS(int x,int y)/// 利用BFS求得 每个定点的最早开始时间 以及最晚时间
{
ve[x] = 0 ;
memset(visit,false, sizeof(visit)) ;
queue<int>L;
stack<Edge> l;/// 利 栈 保存边的相对位置
L.push(x);
while(!L.empty())
{
int t = L.front() ;
L.pop();
if(visit[t]== true)
continue;
visit[t] = true;
int len = G[t].next.size();
for(int i = 0 ; i<len; i++)
{
l.push(E[G[t].next[i]]) ;
int to = E[G[t].next[i]].to;
int data = E[G[t].next[i]].data;
ve[to] = max(ve[to],ve[t] + data);
L.push(to);
}
}
vl[y] = ve[y];
while(!l.empty())
{
Edge t = l.top();
l.pop();
int from = t.from;
int to = t.to;
int data = t.data;
vl[from] = min(vl[from],vl[to]-data);
}
}
void CriticalPath(int x,int y)
{
memset(visit,false,sizeof(visit)) ;
BFS(x,y);
/// for(int i = 0 ;)
/******** 确定最终路线 *****/
p.clear() ;
p.push_back(x) ;
int sum = 0 ;
// for(int i = 1 ;i<= n;i++)
// {
// printf("ve[%d] == %d\n",i,ve[i]);
// }
// cout<<endl;
// for(int i = 1 ;i<= n;i++)
// {
// printf("vl[%d] == %d\n",i,vl[i]);
// }
// printf("关键路径的数据为 : \n");
queue<int > L;
L.push(x) ;
memset(visit,false,sizeof(visit)) ;
while(!L.empty())
{
int t = L.front() ;
L.pop();
if(visit[t] == true)
{
continue;
}
visit[t] = true;
int len = G[t].next.size();
int key = INT_MAX;
bool flag = false;
int data = -1 ;
for(int i = 0 ; i<len; i++)
{
int to = E[G[t].next[i]].to;
int ans = E[G[t].next[i]].data;
int from = E[G[t].next[i]].from ;
//cout<<"from == "<<from<<" , to == "<<to<<" , data == "<<ans<<endl;
// cout<<"ve[from] == "<<ve[from]<<" v1[to] == "<<vl[to]<<endl;
int x = ve[from] , y = vl[to] - ans;
// cout<<"x = "<<x<<" , y == "<<y<<endl;
if(x == y)
{
if(key > to)
{
key = to ;
data = ans;
}
flag = true;
}
}
if(flag == true)
{
//cout<<data<<" ";
sum += data;
L.push(key) ;
p.push_back(key) ;
t = key;
}
}
cout<<sum<<endl;
int len = p.size();
for(int i = 0 ; i<len-1; i++)
{
cout<<p[i]<<" "<<p[i+1]<<endl;
}
}
};
Map a;
bool b[10000+10] = {false}; /// 源点
bool c[10000+10] = {false}; /// 汇点
int main()
{
int n,m;
while(cin>>n>>m)
{
a.Init(n);
memset(b,false,sizeof(b)) ;
memset(c,false,sizeof(c)) ;
for(int i = 0 ; i<m; i++)
{
int x,y,z;
cin>>x>>y>>z;
a.Add_Edge(x,y,z) ;
b[x] = true;
c[y] = true;
}
int x, y ;
for(int i = 1 ; i<= n; i++)
{
if(b[i] == false)
{
y = i;
}
if(c[i] == false)
{
x = i;
}
}
a.CriticalPath(x,y);
}
return 0 ;
}
/**
10 13
1 2 5
1 3 6
2 4 3
3 4 6
3 5 3
4 5 3
4 6 4
5 7 1
5 8 4
7 9 5
8 9 2
6 10 4
9 10 2
9 11
1 2 6
1 3 4
1 4 5
2 5 1
3 5 1
4 6 2
5 7 9
5 8 7
6 8 4
8 9 4
7 9 2
7 10
1 2 3
1 4 6
1 3 2
2 5 4
2 4 2
3 4 1
3 6 3
4 5 1
5 7 3
6 7 4
****/
/***************************************************
User name: 大铁棍子医院铜主任
Result: Accepted
Take time: 104ms
Take Memory: 2048KB
Submit time: 2017-08-23 11:03:37
****************************************************/