传送门:hdu 2647 REward
题目大意
有个老板要给员工发工资,老板很善良想满足每个员工的要求 ,但是他很吝啬。
解题思路
先解释一下什么是向前星邻接表;
一般在一个图当中我们使用邻接矩阵表示连个点之间是否有边比如:E[i][v] = 1表示有边,如果等于0表示没有边,但是如果数据十分大的时候就会造成空间的浪费,而且内存也是不够的。那么我们就考虑使用邻接表了,(邻接表和邻接矩阵都是线性代数里面的概念),邻接表就是一种链表。我们一般是使用的时候是定义一个结构体,如下:
struct Edge{
int v,nxt;//v保存的是被指向的节点,nxt表示头结点保存的边的序号,如果是-1表示指向空
}e[MAXN<<1];//存的是边
简单地来说nxt表示的是next表示与当前边起点一样的另一条边在弄的数组中的位置
比如我们构造一个有向图:
1 2
3 4
2 3
2 4
1 3
前面的指向后面的。对应的图如下:
我们先来看一下添加边的代码:
void initEdge()
{
erear = 0;
memset(head,-1,sizeof head);
memset(OUT,0,sizeof OUT);
}
void addEdge(int u,int v)
{
e[erear].v = v;//开始节点
e[erear].nxt = head[u];//当前节点的下一个节点指向的头节点
//将当前节点变为头节点
head[u] = erear++;
}
那么建图的过程如下:
1. 首先要有”空”边
2. 建立第一条边
3. 因为前三条边都是一样的,所以前三条边建立之后为
4. 当前节点的上一个节点不是空节点了,就可以连接了
5. 建立之后如图:
知道了这个之后我们就是通过头节点来进行访问的,代码如下:
for(int j=head[u];~j;j=e[j].nxt)
{
int v = e[j].v;
...
}
这样优化空间就可以了。
AC代码
#include<cstdio>
#include<cstring>
#include<set>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN = 20000 + 5 ;
int out[MAXN],head[MAXN],erear;
int N,M,OUT[MAXN];
bool vis[MAXN];
struct Edge{
int v,nxt;
}e[MAXN<<1];
void initEdge()
{
erear = 0;
memset(head,-1,sizeof head);
memset(OUT,0,sizeof OUT);
}
void addEdge(int u,int v)
{
e[erear].v = v;
e[erear].nxt = head[u];
head[u] = erear++;
}
int getAns()
{
queue<int>Q;
for(int i=1;i<=N;i++)
if(OUT[i]==0) Q.push(i);
int ranked = 0,ret = -1,sum = 0;
while(!Q.empty())
{
int tmp = Q.size();
ret += tmp*(ranked+888);
for(int i=0;i<tmp;i++)
{
int u = Q.front();
sum++;
Q.pop();
for(int j=head[u];~j;j=e[j].nxt)
{
int v = e[j].v;
if(--OUT[v] == 0)
Q.push(v);
}
}
ranked++;
}
if(sum!=N)return -1;
return ret;
}
int main()
{
int u,v;
while(~scanf("%d%d",&N,&M))
{
initEdge();
for(int i=0;i<M;i++)
{
scanf("%d%d",&v,&u);
addEdge(u,v);
OUT[v]++;
}
int ans = getAns();
printf("%d\n",ans==-1?-1:ans+1);
}
return 0;
}