Hdu2647【拓扑排序】

/*Reward
Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 6488    Accepted Submission(s): 2001


Problem Description
Dandelion's uncle is a boss of a factory. As the spring festival is coming , he wants to distribute rewards to his workers. Now he has a trouble about how to distribute the rewards.
The workers will compare their rewards ,and some one may have demands of the distributing of rewards ,just like a's reward should more than b's.Dandelion's unclue wants to fulfill all the demands, of course ,he wants to use the least money.Every work's reward will be at least 888 , because it's a lucky number.
 

Input
One line with two integers n and m ,stands for the number of works and the number of demands .(n<=10000,m<=20000)
then m lines ,each line contains two integers a and b ,stands for a's reward should be more than b's.
 

Output
For every case ,print the least money dandelion 's uncle needs to distribute .If it's impossible to fulfill all the works' demands ,print -1.
 

Sample Input
2 1
1 2
2 2
1 2
2 1
 

Sample Output
1777
-1
 

Author
dandelion
 

Source
曾是惊鸿照影来 
*/ 
#include<stdio.h>
#include<string.h>
#include<stdlib.h> 
int out_degree[10010], n, m, ans[10010], ini1[10010], ini2[10010], map[10010][20];
//初始化出度为0的集合 
void ini()
{
	ini1[0] = 0;
	for(int i = 1; i <= n; ++i)
	{
		if(!out_degree[i])
		{
			ini1[++ini1[0]] = i;
			ans[i] = 888;//最小的数字 
		}
	}
}

void solve()
{
	//当出度为0的集合非空 
	while(ini1[0])
	{
		ini2[0] = 0;
		//遍历所有点 
		for(int i = 1; i <= ini1[0]; ++i)
		{
			//消除所连边 
			for(int j = 1; j <= map[ ini1[i] ][0]; ++j) 
			{
				out_degree[ map[ ini1[i] ][j] ]--;//消除边 
				m--;//边数-1 
				
				if(out_degree[ map[ ini1[i] ][j] ] == 0)
				{
					ans[ map[ ini1[i] ][j] ] = ans[ ini1[i] ] + 1;
					ini2[++ini2[0]] = map[ ini1[i] ][j];//保存下一层出度为0的结点 
				}
			}
		}
		for(int i = 0; i <= ini2[0]; ++i)
		ini1[i] = ini2[i];
	}
}
int main()
{
	int i, j, k;
	while(scanf("%d%d", &n, &m) != EOF)
	{
		memset(out_degree, 0, sizeof(out_degree));
		memset(ans, 0, sizeof(ans));
		memset(map, 0, sizeof(map));
		for(i = 0; i < m; ++i)
		{
			scanf("%d%d", &j, &k);
			out_degree[j]++;
			map[k][0]++;     //保存每个点的入度边 
			map[k][map[k][0]] = j; 
		}
		ini();
		solve();
		if(m != 0)
		{
			printf("-1\n");
		}
		else
		{
			k = 0;
			for(i = 1; i <= n; ++i)
			k += ans[i];
			printf("%d\n", k);
		}
	}
	return 0;
}


题意:意思很明显,就是给出n个数字以及m对(a,b),a的数字要比b大,然后数字的最小值不得小于888,求n的数字的最小和。

思路:根据拓扑排序的逆向排序,由出度为0的入手赋值为最小值,然后将入度边去除并将相连的点的出度减一,如果出度为0则将其数字赋值为比前一个数字大一并将其作为下一次遍历出度为0的集合中的点。直至出度为0的集合为空为止。在去除边的时候统计边的数目,如果最后与输入的m相符则说明无环,否则说明有环输出-1。

体会:这里其实有个技巧,就是标记相连边的时候如果用n*n的数组去mark的话,需要对每一个点从1~n进行遍历数组,查看是否相连费时费力。这里用的是n*20(这里的20是预估的,即认为每个点的入度边不会超过20条)的数组去统计,每次查询的时候以【n】【0】标记入度边的条数,跟随其后的就是每条入度边的编号,这样就可以直接去除边以及减去对应点的出度,大大的提高了时效。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值