HDOJ 2647 Reward (拓扑排序)

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


你可能感兴趣的:(HDOJ 2647 Reward (拓扑排序))