超级传送门: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; }