PTA—7-11 关键活动
思路
- 很明显的一道关键路径的题(光看名字就知道了吧QAQ)
- 拓扑排序的作用->1.将所有当前父节点(入度为0)压入队列…如果是直接用bfs存放临近的节点就没有全对了QAQ,因为这个点还不一定是父节点(这里的父节点不一定是开始就是父节点,可以是删除了之前的父节点后得到的父节点,也就是删除节点的下一个节点(拗口…))上图…
可见1节点是当前的父节点(入度为0,没有其他边指向他了)
删除后~
可见现在又多出了2,3这两个父节点(入度为0)
继续这样下去就行了~~~
不过可能有些小伙伴会问:
怎么判断之后的节点入度为0啊
很简单,我们每次找着这个父节点的边后,以上图为例
1为父节点,此时入度为0
找它的后面节点(有边),发现2,3这两个节点,然后我们把2,3节点的入度减一
如果当前这个节点减一后入度为0,那么就进入队列咯
不过,可能又有同学会问:
啊哈?我用bfs不也是一样的么?
前面这个样例当然是一样的…
但如果…
这时候,我们第一次只能判断1这个节点两个边,对应2,3咯
如果bfs的话…
此时3节点入度不为0…但是bfs却笨笨地将其放入队列.…
此时
又有人问:
为什么要入度为0???[抠鼻]
你个小笨蛋…不好好看题目,都说了要完成1,5两个任务才能完成3这个任务啦
所以入度为零刚好对应这个情况…
大致思路就是这样惹…
AC代码
注释的地方直接忽略吧…
#include <iostream>
#define inf 100000
using namespace std;
int a[105][105]; //图
int dic[105]; //并查集无法判断有向图的连通
int in[105],out[105];
/*
int Find(int x)
{
if(x == dic[x])
return x;
return dic[x] = Find(dic[x]);
}
bool merge(int x,int y)
{
x = Find(x);
y = Find(y);
if(x != y) //还未连通
{
dic[y] = x;
return true;
}
//已经连通,再连就是环
return false;
}
*/
/*
void ini(int n)
{
for(int i=1;i<=n;i++)
dic[i] = i;
}
*/
int book[105];
void ini(int n)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
a[i][j] = inf;
}
bool dfs(int cur,int n)
{
book[cur] = 1;
for(int i = 1;i<=n;i++)
if(a[cur][i] != inf)
{
if(book[i] == 1 || dfs(i,n))
return true;
}
book[cur] = 0;
return false;
}
/*
int first(int n) //找起点
{
int i;
for(i=1;i<=n;i++)
if(in[i] == 0)
return i;
}
int last(int n) //找终点
{
int i;
for(i=1;i<=n;i++)
if(out[i] == 0)
return i;
}
//后来发现其实可以用出入度来表示起点终点
出度:
就是当前这个点和其他点连线的个数(有向)
入度:
其他点到当前点的个数(有向)
*/
int main()
{
int n,m,i,j;
scanf("%d %d",&n,&m);
ini(n);
for(i=0;i<m;i++)
{
int x,y,v;
scanf("%d %d %d",&x,&y,&v);
a[x][y] = v;
in[y]++;
out[x]++;
}
//int fir = first(n);
//int las = last(n);
//dfs判断是否有环->这里其实也可以用后面第一个拓扑排序来判断...
//具体判断方法就是,记录访问点的个数,如果有环的话,所有的点都不能全部访问,
//或者说,这个环的一个起点永远是个谜(入度不为零)...可以按照1->2 2->3 3->1的方式来模拟
if(dfs(1,n))
{
printf("0");
return 0;
}//dfs判别法
///*
int fast[n+1] = {0}; //最早开始做工的时间
int late[n+1] = {0}; //可以最晚开工的时间
int que[1005],head = 0,tail = 0;
//注意起点不一定是1 也可能多起点
//fast[fir] = 0;
int count = 0; //访问点的个数
for(i=1;i<=n;i++)
if(in[i] == 0)
que[tail++] = i;
while(head != tail)
{
int x = que[head];
head++;
count++;
for(i=1;i<=n;i++)
{
if(a[x][i] != inf)
{
in[i]--;
if(fast[i] < a[x][i] + fast[x])
fast[i] = a[x][i] + fast[x];
if(in[i]==0) que[tail++] = i;
}
}
}
//拓扑排序判别法->与上头的dfs任选其一
if(count != n)
{
printf("0");
return 0;
}
int more = 0;
for(i=1;i<=n;i++)
if(fast[i] > more)
more = fast[i];
printf("%d\n",more);
//开始求最晚开工时间
head = 0,tail = 0;
//que[tail++] = las;
//初始化late
for(i=1;i<=n;i++)
{
late[i] = inf;
if(out[i] == 0)
que[tail++] = i,late[i] = more;
}
//late[las] = fast[las];
while(head != tail)
{
int x = que[head];
head++;
for(i=n;i>=1;i--)
{
if(a[i][x] != inf)
{
out[i]--;
if(late[i] > late[x] - a[i][x])
late[i] = late[x] - a[i][x];
if(out[i] == 0) que[tail++] = i;
}
}
}
//*/
//bfs求late 和 fast答案会错误
//改用拓扑排序
for(i=1;i<=n;i++)
{
for(j=n;j>=1;j--)
{
if(a[i][j] != inf && late[j]-fast[i] == a[i][j])
printf("%d->%d\n",i,j);
}
}
//for(i=0;i<top;i++)
//printf("%d ",path[i]);
return 0;
}
--------------------------------------------------------------------------end