超级传送门:http://acm.hdu.edu.cn/showproblem.php?pid=2647
本题是一个典型的拓扑排序,但是在排序的过程中需要对结点进行分层。拓扑排序采用消去零入度结点法,分层的时候需要注意:当消去一个结点时,其子结点是属于下一层的,应当用数组is_next[]进行标记,防止分层混乱(此处贡献多个WA)。另外,为了方便计算结果,图最好转置建立,即把a->b换成b->a,这样的话第一层的结点员工都是基本工资888,然后第n层的工资数就是888+n-1,再乘以每层的员工数目,即可得到最少需要支付的金额。如果拓扑排序完成后最后仍有剩余结点未消去,则说明剩余结点构成环,题目无解,输出“-1”。
代码如下:
#include<stdio.h>
#include<string.h>
#define N 10005
#define M 20005
int n,m;
int w[N][20],num[N],r[N],vis[N],div[N],d_num,is_next[N];
int main () {
int i,j,k,h,a,b,ans,cur,ok;
while (scanf("%d%d",&n,&m) != EOF) {
memset(num,0,sizeof(num));
memset(vis,0,sizeof(vis));
memset(r,0,sizeof(r));
memset(div,0,sizeof(div));
for (i=1;i<=m;i++) {
scanf("%d%d",&a,&b);
w[b][++num[b]] = a;
r[a] ++;
}
d_num = 0;
for (k=1;k<=n;k++) {
++d_num;
memset(is_next,0,sizeof(is_next));
for (i=1;i<=n;i++) {
if (!r[i] && !vis[i] && !is_next[i]) {
ok = 1;
for (j=1;j<=num[i];j++) {
r[w[i][j]] --;
if (r[w[i][j]]<0) r[w[i][j]] = 0;
is_next[w[i][j]] = 1;
}
vis[i] = 1;
div[d_num]++;
}
}
}
ok = 1;
cur = 888;
ans = 0;
for (i=1;i<=n;i++) {
if (!vis[i]) {
ok = 0;
break;
}
}
if (ok) {
for (i=1;i<=d_num;i++) {
if (div[i]) {
ans += div[i]*cur++;
}
}
printf("%d\n",ans);
} else printf("-1\n");
}
return 0;
}