【Tarjan】+【SPFA】【APIO2009】Atm

一、算法介绍

tarjan——求解有向图强连通分量。这个算法在本人的一篇blog中有介绍,这里就不赘述了。贴上介绍tarjan的的blog链接:http://www.cnblogs.com/Maki-Nishikino/p/5866191.html

那么接下来说说SPFA:

SPFA全称Shortest Path Faster Algorithm,用于求解单源最短路。既然名字中有“Faster”,那它就一定有过人之处,事实上它也的确比Dijkstra和Bellman-Ford更高效。

它的思路大致如下:
1、先用邻接表把图存下来,并且规定一个数组d,d[i]表示起点到i的最短路程;

2、建立一个队列,将起点放入队列;

3、对队首元素执行松弛操作,遍历所有以队首元素为起点的边,如果被遍历的边可以使到被遍历的边的终点的路径变短,那么就更新这个最短路径,并把被遍历的边的终点放到队尾;

4、每完成一次松弛,就令队首元素出队,重复2,直到队列里没有元素。

原谅博主懒得贴伪代码,我就直接讲题了,反正题解里也有模板#手动滑稽

二、APIO2009 Atm题解

原题链接(来自bzoj):http://www.lydsy.com/JudgeOnline/problem.php?id=1179

题目描述:

输入:

第一行包含两个整数N、M。N表示路口的个数,M表示道路条数。接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路的起点和终点的路口

编号。接下来N行,每行一个整数,按顺序表示每个路口处的ATM机中的钱数。接下来一行包含两个整数S、P,S表示市中心的编号,也就是出发的路口。P表示酒吧数目。接下来

的一行中有P个整数,表示P个有酒吧的路口的编号。

输出:

输出一个整数,表示Banditji从市中心开始到某个酒吧结束所能抢劫的最多的现金总数。

样例输入:

6 7

1 2

2 3

3 5

2 4

4 1

2 6

6 5

10

12

8

16

1

5

1 4
4

3

5

6

样例输出:

47

数据范围:

50%的输入保证N, M<=3000。所有的输入保证N, M<=500000。每个ATM机中可取的钱数为一个非负整数且不超过4000。输入数据保证你可以从市中心沿着Siruseri的单向的道路到达其中的至少一个酒吧。



对于这道题,我们考虑先用tarjan求出它的所有强连通分量,再把同嘤一个强连通分量上的ATM机的钱加起来,让一个强连通分量上的点缩成一个点。然后以市中心s为起点,用SPFA跑出s到其他点的最长(最有价值)路,比较酒吧所在点的d值,输出大的即可。

附上代码:

  1 #include<stdio.h>
  2 #include<algorithm>
  3 #include<string.h>
  4 using namespace std;
  5 struct node
  6 {
  7     int v;
  8     int next;
  9     int w;
 10 };
 11 int n,m;
 12 node e[500010],map[500010];//邻接表存图
 13 int st[500010],head[500010],cnt;
 14 int atm[500010],money[500010];
 15 int d[500010],q[500010];//最短路径&SPFA要用的队列
 16 void build(int a,int b)
 17 {
 18     e[++cnt].v=b;
 19     e[cnt].next=st[a];
 20     st[a]=cnt;
 21 }//建图找强连通分量
 22 int stack[500010],top;//tarjan需要的栈
 23 int dfn[500010],low[500010],dex;//时间戳(深搜序)、可回溯到的最早栈中时间戳、次序编号
 24 bool vis[500010];//tarjan时判断点是否在栈中,SPFA时判断点是否在队列中
 25 int color[500010],num;//表示同一强连通分量上的点
 26 void tarjan(int x)//tarjan找强连通分量
 27 {
 28     dfn[x]=++dex;
 29     low[x]=dex;
 30     vis[x]=true;
 31     stack[++top]=x;//当前点入栈
 32     int i;
 33     for(i=st[x];i!=0;i=e[i].next)//枚举以当前点为起点的边
 34     {
 35         int temp=e[i].v;//temp为当前被枚举边的终点
 36         if(!dfn[temp])//如果当前边终点未被处理
 37         {
 38             tarjan(temp);
 39             low[x]=min(low[x],low[temp]);
 40         }
 41         else if(vis[temp])low[x]=min(low[x],dfn[temp]);
 42     }
 43     if(dfn[x]==low[x])
 44     {
 45         vis[x]=false;
 46         color[x]=++num;//标记当前强连通分量内的点
 47         while(stack[top]!=x)//栈顶元素依次出栈
 48         {
 49             color[stack[top]]=num;
 50             vis[stack[top--]]=false;
 51         }
 52         top--;
 53     }
 54 }
 55 void add()// 把同一强连通分量上的点缩成一个点,把这些点连成一张新图
 56 {
 57     cnt=0;
 58     int i,j;
 59     for(i=1;i<=n;i++)
 60     {
 61         for(j=st[i];j!=0;j=e[j].next)
 62         {
 63             int temp=e[j].v;
 64             if(color[i]!=color[temp])
 65             {
 66                 map[++cnt].v=color[temp];
 67                 map[cnt].next=head[color[i]];
 68                    head[color[i]]=cnt;
 69             }
 70         }
 71
 72     }
 73 }
 74 void spfa(int x)//SPFA找最长路
 75 {
 76     memset(vis,false,sizeof(vis));
 77     int l=1,r=1;
 78     q[l]=x;//初始点放入队列
 79     vis[x]=true;
 80     d[x]=money[x];
 81     while(l<=r)
 82     {
 83         int u=q[l++];
 84         for(int i=head[u];i!=0;i=map[i].next)//遍历所有以当前点为起点的边
 85         {
 86             int v=map[i].v;
 87             if(d[v]<d[u]+money[v])
 88             {
 89                 d[v]=d[u]+money[v];
 90                 if(vis[v])continue;
 91                 q[++r]=v;//如果当前拓展的边的终点不在队列里,就把它放入队尾
 92                 vis[v]=true;
 93             }
 94         }
 95         vis[u]=false;
 96     }
 97 }
 98 int main()
 99 {
100     int a,b,i,s,p,o,ans=0;
101     scanf("%d%d",&n,&m);
102     for(i=1;i<=m;i++)
103     {
104         scanf("%d%d",&a,&b);
105         build(a,b);
106     }//建初始图
107     for(i=1;i<=n;i++)
108     {
109         if(!dfn[i])tarjan(i);//找强连通分量
110     }
111     add();//建新图
112     for(i=1;i<=n;i++)
113     {
114         scanf("%d",&atm[i]);
115         money[color[i]]+=atm[i];
116     }
117     scanf("%d%d",&s,&p);
118     spfa(color[s]);//找单源最短路
119     for(i=1;i<=p;i++)
120     {
121         scanf("%d",&o);
122         ans=max(ans,d[color[o]]);//找到以酒吧为终点的最长路
123     }
124     printf("%d",ans);
125     return 0;
126 }

APIO2009 Atm

时间: 2024-10-29 19:06:49

【Tarjan】+【SPFA】【APIO2009】Atm的相关文章

POJ 1860——Currency Exchange——————【最短路、SPFA判正环】

Currency Exchange Time Limit:1000MS     Memory Limit:30000KB     64bit IO Format:%I64d & %I64u Submit Status Practice POJ 1860 Description Several currency exchange points are working in our city. Let us suppose that each point specializes in two par

POJ 3621 Sightseeing Cows 【01分数规划+spfa判正环】

题目链接:http://poj.org/problem?id=3621 Sightseeing Cows Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 11526   Accepted: 3930 Description Farmer John has decided to reward his cows for their hard work by taking them on a tour of the big ci

POJ 1904:King&#39;s Quest【tarjan】

题目大意:给出一个二分图的完美匹配(王子和公主的烧死名单表),二分图x部和y部均只有n个点,问对于每一个x部的点,他能选择哪些点与之匹配 使得与之匹配后,剩余图的最大匹配仍然是n 思路:这题是大白书379页二分图的压轴题,在图论刷的题还不多时思考过这题,现在想来也不难想 这题引人瞩目的一点便是预先给出了一个二分图的初始匹配 对每个点枚举后增广显然不怎么可行,那么还是图论问题的经典思考方式,点和边各表示什么 题目的输入天然的给出了一个图,但对这题好像没什么用处,于是开始思考把给出的初始匹配的每条边

USACO 2008 JAN Telephone Lines 【二分答案、SPFA】

题目 题目描述 Farmer John wants to set up a telephone line at his farm. Unfortunately, the phone company is uncooperative, so he needs to pay for some of the cables required to connect his farm to the phone system. There are N (1 ≤ N ≤ 1,000) forlorn telep

洛谷 P2194 HXY烧情侣【Tarjan缩点】 分析+题解代码

洛谷 P2194 HXY烧情侣[Tarjan缩点] 分析+题解代码 题目描述: 众所周知,HXY已经加入了FFF团.现在她要开始喜(sang)闻(xin)乐(bing)见(kuang)地烧情侣了.这里有n座电影院,n对情侣分别在每座电影院里,然后电影院里都有汽油,但是要使用它需要一定的费用.m条单向通道连接相邻的两对情侣所在电影院.然后HXY有个绝技,如果她能从一个点开始烧,最后回到这个点,那么烧这条回路上的情侣的费用只需要该点的汽油费即可.并且每对情侣只需烧一遍,电影院可以重复去.然后她想花尽

【Python中if __name__ == &#39;__main__&#39;: 的解析】

在很多Python代码中,在代码的最下方会看到  if __name__ == '__main__':,这段代码到底有什么用呢? 在理解这个语句的作用前,需要知道的是,一般的Python文件后缀为.py,其可以拿来执行,也可以用来作为模块使用import导入.当Python解析器读取一个源文件时它会执行所有的代码.在执行代码前会定义一些特殊的变量.如果解析器运行的模块(源文件)作为主程序,它将会把__name__变量设置成"__main__".如果只是引入其他的模块,__name__变

【凯子哥带你学Android】Andriod性能优化之列表卡顿——以“简书”APP为例

这几天闲得无聊,就打开手机上的开发者模式里面的"GPU过度绘制"功能,看看别家的App做的咋样,然后很偶然的打开了"简书",然后就被它的过度绘制惊呆了,于是写了这篇性能分析的文章,从一个只有APK文件的角度,说下如何寻找布局中可能存在的性能问题,以及解决方案.本文章以简书Android最新版本1.9.1进行分析. GPU过度绘制 Hierarchy View SysTrace TraceView 总结 分析资源下载 GPU过度绘制 首先打开下面两个功能开关 开发者模

类型转换、运算符、位运算符【以及原码、反码、补码】

1.类型转换 php中的‘+’与js有区别,php中+只是算术运算符[更偏向转化为数字].js更偏向转化为字符串 php本身的自动转换类型便符合大多数对类型的处理.[也有强制转换的情形出现] [注意转换关系:字符串转换成数字类型,开头的那部分字符串能够转化为数字(还要判断浮点型和整型)] 2.转换成布尔型[实现流程控制的关键] 以下值为false: (1)布尔值为false (2)整型值为0 (3)浮点型为0.0 (4)空字符串[字符串'0'(相当于是字符串做数组时是一个空字符串),区别,注意‘

文件载入流程,函数的使用【函数名,参数【按引用赋值的问题】,函数体,【变量作用域和生命周期】】

1.求最大公约数辗转相除求值例如:12,8求最大公约数12%8=4[不为零]8%4=0[为零,则4为最大公约数][原理就是12,8的最大公约数和8,4的最大公约数一致][这个问题已经可被证明]计算机中最早的算法 2.文件载入---流程[php执行的先后顺序][php对代码的编译以文件为单位]先检查一个文件中的语法再进行编译然后才会一行行解释执行 [这一点需要注意]这里进行文件载入的流程讲解:php文件载入时,被引入的文件中的语法并不会解析,只有当该文件被执行[也就是被引入语句之后的部分才会报错]

【C语言天天练(二三)】errno变量

引言: 在C编程中,errno是个不可缺少的变量,特别是在网络编程中.如果你没有用过errno,那只能说明你的程序不够健壮. 为什么会使用errno呢?这是系统库设计中的一个无奈之举,他更多的是个技巧,而不是架构上的需要.我们观察下函数结构,可以发现,函数的参数返回值只有一个,这个返回值一般可以携带错误信息,比如负数表示错误,而正数表述正确的返回值,比如recv函数.但是对于一些返回指针的函数,如:char *get_str():这个方法显然没有用的.NULL可以表示发生错误,但是发生什么错误却