• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
Na+
Acoder's Space 沙茶一只,求各路大牛指点!
博客园    首页    新随笔    联系   管理    订阅  订阅

前向星 V2.0——以及USACO3.2.6 Sweet Butter

  以前我曾经学习过前向星,充分感觉到了他的好用以及编程复杂度小的优点。鉴于好久没有写程序,前向星也忘得差不多了,今天重新写篇博文来谈谈前向星的问题(耐心看,今天说的前向星有些不一样)

  先介绍一下前向星(不是星星啊!),前向星是一个边集数组,把边的起点终点和权值存起来,然后以起点从小到大或者从大到小排序,记录每个顶点在数组中的起始位置和长度...以达到节省空间的目的。

  代码就自己随便找找吧,有的是!

  咱今天重点谈的的确是前向星,此前向星非彼前向星,我就不卖关子了,咱直接说吧:前向星升级版——链式前向星

(来俩沙茶把这人抬走)。

  这个东西是从Malash大犇那里学来的,非常NB的东西哦~!

  普通前向星好是好,可是如果数据规模大,排序他也不是O(1)时间,也许你排个序就把时间耽误了,不合适,这时候

用邻接矩阵存不下,你还不会写邻接链表,怎么办呢?链式前向星!

  先介绍一下链式前向星的组成:

  HEAD数组:记录每个顶点的起点位置

  E数组:记录每条边的终点

  NEXT数组:记录下一条边的位置

  其他的诸如权值什么的,自己往上加,这三个数组是必须的。

  拿Malash神牛的课件咱来举例说明一下链式前向星的实现

  假设有向图边的输入顺序为:

  1 2  

  2 3

  3 4

  1 3

  4 1

  1 5

  4 5

  那么我们来WATCH一下三个数组的状态(我去现写个程序 你们等会)

  head[1]=6  head[2]=2  head[3]=3  head[4]=7   head[5]=0
  e[1]=2       e[2]=3       e[3]=4        e[4]=3        e[5]=1       e[6]=5        e[7]=5
  next[1]=0  next[2]=0  next[3]=0   next[4]=1   next[5]=0   next[6]=4   next[7]=5

  恩,大致看懂了吧,这就是链式前向星的存储方式,下面讲一下查询方法

  假设你要查与1相连的边,那么你先访问head[1]=6 根据e[6]你得到了边1 5

  继续访问next[6]=4,得到了边1 3,继续访问next[4]=1,这时候你得到了边1 2,next[1]=0,访问结束.

  嗯,SO EASY!如果你想访问第N条边,你可以加个计数器,想怎么发挥就随意咯~

  我们来简单的代码实现一下

procedure insertedge(a,b,i:longint); //将边(a,b)插入到第I个位置
begin
		next[i]:=head[a]; //记录NEXT[I]
		e[i]:=b;     //记录终点
		head[a]:=i;   //将HEAD[A]替换成新的
end;

  下面是查询操作,以输出与定点V连接的点为例

procedure print(v:longint); //过程名随意改,你改成main什么的都行
var i:longint;
begin
	i:=head[v];
	while i <> 0 do
	begin
		write(e[i],' ');  //在这里想对边干什么都可以...注意分寸= =
		i:=next[i];
	end;
end;

 下面来介绍一下无向边存储,其实很简单,最简单的方法是把刚才插入有向边的过程调用两遍

procedure insertundirectededge(a,b,i:longint); //将第I条无向边加入前向星数组
begin
	insertedge(a,b,i shl 1-1); //i shl 1 = i << 1 = i*2
	insertedge(b,a,i shl 1);//这就是猥琐地调用给过的程序的方法= =BS我吧- -
end;

  这个就是前向星V2.0------链式前向星!最大的好处就是不用排序了,拿省下来的排序时间去买零食去吧!

(莫拍砖)

  下面介绍一下链式前向星的简单应用,以USACO3.2.6 Sweet Butter 为例

 题干:http://www.nocow.cn/index.php/Translate:USACO/butter (你去看鸟文也是可以的)

 这道题乍一看及其闹心,一堆牛来回走,牧场的牛总是动态变化的,也没法算啊...(至少我一开始的时候

是这样想的,不要BS) 仔细一分析,把牛一波一波的分开算,假设你就求第一个牧场里的牛到各个点的时间,

那么这时候你假设其他牧场都没有牛,其他点亦是如此,然后把各个牧场的牛到同一牧场花费的时间累加,

那么这道题就清晰地成为了多源最短路的问题,可以方便的用SPFA进行求解。

  既然已经知道了链式前向星,那么就以它为实例吧,下面是我的程序,诸位大牛莫BS。

program butter;//上面的内容略掉
var n,p,r,ans:longint;
    k,sum,dis,head:array[0..800] of longint;//k存的各牧场奶牛数,sum存所有牛到牧场的总时间
    vis:array[0..800] of boolean;
    e,next,w:array[0..2900] of longint;
    q:array[0..100000] of integer;//队列,以防万一,开大点
procedure insertedge(a,b,i,v:longint);
begin
	next[i]:=head[a];
	e[i]:=b;
	w[i]:=v;
	head[a]:=i;
end;
procedure pre;
var i,t,x,y,z:longint;
begin
	readln(n,p,r);
	fillchar(k,sizeof(k),0);
	fillchar(head,sizeof(head),0);
	fillchar(next,sizeof(next),0);
	for i:=1 to n do
	begin
		readln(t);
		inc(k[t]);
	end;
	for i:=1 to r do
	begin
		readln(x,y,z);
		insertedge(x,y,i shl 1-1,z);
		insertedge(y,x,i shl 1,z);
	end;
	fillchar(sum,sizeof(sum),0);
	ans:=maxlongint;
end;

procedure spfa(s:longint);
var hd,tl,now,cnt,i:longint;
begin
	fillchar(vis,sizeof(vis),false);
	for i:=1 to p do dis[i]:=maxlongint;
	dis[s]:=0;
	q[1]:=s;
	hd:=0;tl:=1;
	vis[s]:=true;
	cnt:=1;
	while cnt <> 0 do
	begin
		hd:=hd mod 100000+1;
		now:=q[hd];
		dec(cnt);
		i:=head[now];
		while i <> 0 do
		begin
			if dis[e[i]] > dis[now]+w[i]*k[s] then//w[i]*k[s]表示这些牛花的总时间
			begin
				dis[e[i]]:=dis[now]+w[i]*k[s];
				if not vis[e[i]] then
				begin
					vis[e[i]]:=true;
					tl:=tl mod 100000+1;
					q[tl]:=e[i];
					inc(cnt);
				end;
			end;
			i:=next[i];
		end;
		vis[now]:=false;
	end;
end;

procedure sol;
var i,j:longint;
begin
	for i:=1 to p do
	if k[i] <> 0 then
	begin
		spfa(i);
		for j:=1 to p do
		sum[j]:=sum[j]+dis[j]; //进行叠加
	end;
	for i:=1 to p do
	if ans > sum[i] then ans:=sum[i]; //不解释了
end;

procedure outprint;
begin
	writeln(ans);
end;

procedure main;
begin
	pre;
	sol;
	outprint;
end;

begin
	assign(input,'butter.in');reset(input);	
	assign(output,'butter.out');rewrite(output);
	main;
	close(input);close(output);
end.
Executing...
Test 1: TEST OK [0.000 secs, 516 KB]
Test 2: TEST OK [0.000 secs, 516 KB]
Test 3: TEST OK [0.000 secs, 516 KB]
Test 4: TEST OK [0.000 secs, 516 KB]
Test 5: TEST OK [0.000 secs, 516 KB]
Test 6: TEST OK [0.000 secs, 516 KB]
Test 7: TEST OK [0.027 secs, 516 KB]
Test 8: TEST OK [0.054 secs, 516 KB]
Test 9: TEST OK [0.108 secs, 516 KB]
Test 10: TEST OK [0.162 secs, 516 KB]
我其实开始不是用SUPER前向星写的,而是用类似邻接矩阵的存储方式 时间效率差不多可是空间是2360KB,这个是
那个的四分之一,可见是非常省空间的!
posted @ 2011-04-06 09:57  Na+  阅读(1137)  评论(1)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3