/*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】标记入度边的条数,跟随其后的就是每条入度边的编号,这样就可以直接去除边以及减去对应点的出度,大大的提高了时效。