题目大意
有n个工程需要完成,对于第i个工程,完成需要T[i]的时间,各个工程可以同时进行。
在给你一个矩阵,描述第i个工程与第j个工程是否有依赖关系。对于这些依赖关系,在被依赖的工程完成前,此工程不能进行。若整个工程无法完成,那么输出-1。若可以完成,那么输出关键子工程。
关键子工程的定义是,只要某个工程一延期,就会导致整个工程的完成时间延后,那么就称它为关键子工程。
输入样例
5
5 4 12 7 2
0 0 0 0
0 0 0 0
0 0 0 0
1 1 0 0
1 1 1 1
输出样例
14
1 3 4 5
题解
思路
先用拓扑排序,可以判定这个图在中是否有环,若有环,则这个工程无法完成,输出-1,退出程序。
可以完成则进行dp,第一遍dp维护每个工程的最早完成时间,由此可以计算出整个工程的完成时间,第二遍dp维护每个工程在不影响整个工程的完成时间的情况下的最晚完成时间。若某个工程的最早完成时间与最晚完成时间相等,那么按照定义,它就是关键子工程。
代码
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
const int size=210;
int n,p,now,ans,a[size][size],w[size],in[size],top[size],fast[size],slow[size];
bool vis[size];
queue<int> q;
int min(int x,int y){return x<y?x:y;}
int max(int x,int y){return x>y?x:y;}
int main()
{
freopen("project.in","r",stdin);
freopen("project.out","w",stdout);
scanf("%d\n",&n);
for(int i=1;i<=n;i++)
scanf("%d ",&w[i]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i!=j)
{
scanf("%d ",&a[i][j]);
if(a[i][j])
in[i]++;
}
for(int i=1;i<=n;i++)//拓扑排序
if(!in[i])
q.push(i);
while(!q.empty())
{
now=q.front();
q.pop();
top[++p]=now;
vis[now]=true;
for(int i=1;i<=n;i++)
if(a[i][now]&&in[i])
{
in[i]--;
if(!in[i])
q.push(i);
}
}
for(int i=1;i<=n;i++)
if(!vis[i])//若还有没有访问的节点
{
puts("-1");
return 0;
}
for(int i=1;i<=n;i++)
{
now=top[i];//按照拓扑序循环
for(int j=1;j<=n;j++)
if(a[now][j])
fast[now]=max(fast[now],fast[j]);
fast[now]+=w[now];
ans=max(ans,fast[now]);
}
printf("%d\n",ans);
for(int i=1;i<=n;i++)
slow[i]=ans;
for(int i=n;i>=1;i--)
{
now=top[i];
for(int j=1;j<=n;j++)
if(a[j][now])
slow[now]=min(slow[now],slow[j]-w[j]);
}
for(int i=1;i<=n;i++)
if(fast[i]==slow[i])
printf("%d ",i);
printf("\n");
return 0;
}