【SPFA与Dijkstra的对比】CDOJ 1961 咸鱼睡觉觉【差分约束-负权最短路径SPFA】
差分约束系统,求最小值,跑最长路。
转自:https://www.cnblogs.com/ehanla/p/9134012.html
题解:设sum[x]为前x个咕咕中至少需要赶走的咕咕数,则sum[b]−sum[a−1]>=c表示[a,b]区间至少赶走c只。题目中选择的是最少,我们需要跑最长路,因存在负边,所以以SPFA进行操作。
d[v]>=d[u]+w,前面我们可以推出第一个式子sum[b]>=sum[a−1]+c,但是如果只连这些边,整张图连通不起来。我们发现i和i+1存在关系0<=sum[i+1]−sum[i]<=1,这个其实就是表示i+1那个位置赶与不赶。
推出第二个和第三个式子:sum[i]>=sum[i+1]−1,sum[i+1]>=sum[i]+0
由以上式子得到边:a−1点 b点 距离c
i+1点 i点 距离−1
i点 i+1点 距离0
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
typedef long long LL;
const int N=2e5+;
int n,k,cnt;
bool vis[N];
int head[N],d[N];
struct node
{
int to,next,w;
}edge[N]; void init()
{
cnt=;
memset(head,-, sizeof(head));
} void add(int u,int v,int w)
{
edge[cnt].to=v;edge[cnt].w=w,edge[cnt].next=head[u];head[u]=cnt++;
} void spfa()
{
memset(d,-INF,sizeof(d));
memset(vis,,sizeof(vis));
queue<int> q;
q.push();
d[]=;
vis[]=;
while(q.size())
{
int u=q.front();q.pop();
vis[u]=; // 可以重复入队,现在不在队内
for(int i=head[u];i!=-;i=edge[i].next)
{
int v=edge[i].to;
if(d[v]<d[u]+edge[i].w) // 最长路径
{
d[v]=d[u]+edge[i].w;
if(!vis[v])
{
q.push(v);
vis[v]=;
}
}
}
}
} int main()
{
init();
cin>>k>>n;
for(int i=;i<=n;i++)
{
int a,b,c;
cin>>a>>b>>c;
// d[b]-d[a-1] >= c
add(a-,b,c);
}
// 0 <= d[i+1]-d[i] <= 1
for(int i=;i<k;i++)
{
add(i,i+,);
add(i+,i,-); // 存在负数权值
}
spfa();
cout<<d[k]<<endl;
return ;
}
求单源最短路的算法SPFA:
算法中需要的变量
int N; //表示n个点,从1到n标号
int s,t; //s为源点,t为终点
int d[N]; //d[i]表示源点s到点i的最短路
int pre[N]; //记录路径(或者说记录前驱)
queue <int> q; //队列,
bool vis[N]; //vis[i]=1表示点i在队列中 vis[i]=0表示不在队列中
在整个算法中有顶点入队要记得标记vis数组,有顶点出队记得消除那个标记,顶点可以重复入队,这区别于dijkstra算法
队列+松弛操作:
读取队头顶点u,并将队头顶点u出队(消除标记);将与点u相连的所有点v进行松弛操作,如果能更新估计值(使d[v]变小),那么就更新,另外,如果点v没有在队列中,那么要将点v入队(记得标记),如果已经在队列中了,那么就不用入队
以此循环,直到队空为止就完成了单源最短路的求解
SPFA可以处理负权边
SPFA判断负环:如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)
int spfa_bfs(int s)
{
queue<int> q;
memset(d,0x3f, sizeof(d)); // 距离矩阵
memset(c,,sizeof(c)); // 入队次数
memset(vis,, sizeof(vis));
q.push(s);
vis[s]=;
c[s]++;
int OK=;
while(q.size())
{
int u=q.front();q.pop();
vis[u]=;
for(int i=head[u];i;i=next[i])
{
int v=ver[i];
if(d[v]>d[u]+w[i])
{
d[v]=d[u]+w[i];
if(!vis[v])
{
vis[v]=;
q.push(v);
c[v]++;
if(c[v]>N) // 超过入队次数上限,说明有负环
return OK=;
}
}
}
}
return OK;
}
Dijkstra的priority_queue写法与SPFA的queue写法对比:
- Dijkstra+heap是用小根堆,每次取出d中未访问的最小点,来更新距离,对于这个点来说,最小距离就是当前的d。
- SPFA是用双端队列,每次取出队头,来更新距离,它之后可能还会入队。是一种动态逼近法,因为每次松弛距离都会减小,所以松弛一定会有结束。如果一个点入队超过n次就存在负环。
Dijkstra
用STL中的优先队列实现堆:
while(优先队列非空)
-->队头出队,松弛它的边
-->松弛了的pair<新距离,点>入队
typedef pair<int,int> P;
priority_queue<P,vector<P>,greater<P> > q; // 最小堆
...
while(!q.empty()){ // O(V) 加上count<n可以优化一点点
int w=q.top().first, u=q.top().second;
q.pop(); // O(lgV)
if(vis[u])continue; vis[u]=true;
//++count;
for(int i=head[u];i;i=edge[i].next){ // Sum -> O(E)
int v=edge[i].to;
if(d[v]>d[u]+edge[i].w){
d[v]=d[u]+edge[i].w;
q.push(P(d[v],v)); // O(lgV)
}
}
}
SPFA:
while(队非空)
-->队头出队,取消标记,松弛它的边
-->松弛了且不在队内的点入队,并标记
while(!q.empty()){
int u=q.front(); q.pop();
vis[u]=false;
for(int i=head[u];i;i=edge[i].next){
int v=edge[i].to;
if(d[v]>d[u]+edge[i].w){
d[v]=d[u]+edge[i].w;
if(!vis[v])vis[v]=true,q.push(v);
}
}
}
Dijkstra和Prim也很相似,它们的区别主要是d的含义,前者是到s的临时最短距离[s->每一点],后者是到树的临时最短距离[边长],相同点是,每次找最小的d更新其它点的距离。
【SPFA与Dijkstra的对比】CDOJ 1961 咸鱼睡觉觉【差分约束-负权最短路径SPFA】的更多相关文章
- POJ.1752.Advertisement(差分约束 最长路SPFA)
题目链接 \(Description\) 有\(n\)个人在一条直线上跑步,每个人的起点 \(Si\).终点 \(Ei\) 已知:每个点可以放一个广告牌,一个人\(i\)能看到的广告牌数量为 \(Ei ...
- HDU.1529.Cashier Employment(差分约束 最长路SPFA)
题目链接 \(Description\) 给定一天24h 每小时需要的员工数量Ri,有n个员工,已知每个员工开始工作的时间ti(ti∈[0,23]),每个员工会连续工作8h. 问能否满足一天的需求.若 ...
- POJ 3159 Candies 还是差分约束(栈的SPFA)
http://poj.org/problem?id=3159 题目大意: n个小朋友分糖果,你要满足他们的要求(a b x 意思为b不能超过a x个糖果)并且编号1和n的糖果差距要最大. 思路: 嗯, ...
- [ACM] 最短路算法整理(bellman_ford , SPFA , floyed , dijkstra 思想,步骤及模板)
以杭电2544题目为例 最短路 Problem Description 在每年的校赛里,全部进入决赛的同学都会获得一件非常美丽的t-shirt. 可是每当我们的工作人员把上百件的衣服从商店运回到赛场的 ...
- POJ 3159 Candies 解题报告(差分约束 Dijkstra+优先队列 SPFA+栈)
原题地址:http://poj.org/problem?id=3159 题意大概是班长发糖果,班里面有不良风气,A希望B的糖果不比自己多C个.班长要满足小朋友的需求,而且要让自己的糖果比snoopy的 ...
- 图论最短路径算法总结(Bellman-Ford + SPFA + DAGSP + Dijkstra + Floyd-Warshall)
这里感谢百度文库,百度百科,维基百科,还有算法导论的作者以及他的小伙伴们...... 最短路是现实生活中很常见的一个问题,之前练习了很多BFS的题目,BFS可以暴力解决很多最短路的问题,但是他有一定的 ...
- 图论算法》关于SPFA和Dijkstra算法的两三事
本来我是想把这两个算法分开写描述的,但是SPFA其实就是Dijkstra的稀疏图优化,所以其实代码差不多,所以就放在一起写了. 因为SPFA是Dijkstra的优化,所以我想来讲讲Dijkstra. ...
- luogu P3371 & P4779 单源最短路径spfa & 最大堆优化Dijkstra算法
P3371 [模板]单源最短路径(弱化版) 题目背景 本题测试数据为随机数据,在考试中可能会出现构造数据让SPFA不通过,如有需要请移步 P4779. 题目描述 如题,给出一个有向图,请输出从某一点出 ...
- 关于SPFA Bellman-Ford Dijkstra Floyd BFS最短路的共同点与区别
关于模板什么的还有算法的具体介绍 戳我 这里我们只做所有最短路的具体分析. 那么同是求解最短路,这些算法到底有什么区别和联系: 对于BFS来说,他没有松弛操作,他的理论思想是从每一点做树形便利,那么时 ...
随机推荐
- 【转】thread.sleep(0)与thread.sleep(1)的区别
Thread.Sleep(0) Sleep的意思是告诉操作系统自己要休息n毫秒,这段时间就让给一个就绪的线程吧.当n=0时,意思是要放弃自己剩下的时间片,但是仍然是就绪状态.Sleep(0)只允许那些 ...
- Springboot:开发时工程启动慢
个人的解决办法是删除workspace中的histor 然后重启Eclipse,发现一切正常.
- Thread Synchronization Queue with Boost
介绍:当开发一个多线程程序时,同步是一个很大的问题.如果你的程序需要数据流包,那么用队列是个好办法. 你可以在 http://www.boost.org/ 发现 boost 库和文档,从它的网站可以看 ...
- [MySQL]InnoDB引擎的行锁和表锁
1.行锁和表锁 在mysql 的 InnoDB引擎支持行锁,与Oracle不同,mysql的行锁是通过索引加载的,即是行锁是加在索引响应的行上的,要是对应的SQL语句没有走索引,则会全表扫描, 行锁则 ...
- 修改oracle数据库允许连接的数
当前连接数:select count(*) from v$process;查询数据库允许的最大连接数: select value from v$parameter where name = 'proc ...
- oracle提高查询效率的34条方法
注:本文来源:远方的守望者 <oracle提高查询效率的34条方法> oracle提高查询效率的34条方法 1.选择最有效率的表名顺序 (只在基于规则的优化器中有效): ORACLE的解 ...
- 【JS】中的原型prototype到底是个啥
一.什么是原型 原型prototype是函数的一个属性,这个属性是一个指针,指向一个对象(原型对象),这个原型对象的用途是包含可以由特定类型的所有实例共享的属性和方法. 函数也是一种对象.它也是属性的 ...
- 第十四单元 Linux网络原理及基础设置
·ifconfig命令来维护网络(详见linux系统管理P422) 1) 掌握ifconfig命令的功能:显示所有正在启动的网卡的详细信息或设定系统中网卡的IP地址.2) 灵活应用ifconfig命令 ...
- 最短路径之Bellman-Ford算法
第一行为源点个数,边的个数m 接下来m行为a->b和权值 5 52 3 21 2 -31 5 54 5 23 4 3 Bellman-Ford算法(1): #include<iostrea ...
- LeetCode(74):搜索二维矩阵
Medium! 题目描述: 编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值.该矩阵具有如下特性: 每行中的整数从左到右按升序排列. 每行的第一个整数大于前一行的最后一个整数. 示例 ...