差分约束系统,求最小值,跑最长路。

转自: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);
}
}
}

DijkstraPrim也很相似,它们的区别主要是d的含义,前者是到s的临时最短距离[s->每一点],后者是到树的临时最短距离[边长],相同点是,每次找最小的d更新其它点的距离。

【SPFA与Dijkstra的对比】CDOJ 1961 咸鱼睡觉觉【差分约束-负权最短路径SPFA】的更多相关文章

  1. POJ.1752.Advertisement(差分约束 最长路SPFA)

    题目链接 \(Description\) 有\(n\)个人在一条直线上跑步,每个人的起点 \(Si\).终点 \(Ei\) 已知:每个点可以放一个广告牌,一个人\(i\)能看到的广告牌数量为 \(Ei ...

  2. HDU.1529.Cashier Employment(差分约束 最长路SPFA)

    题目链接 \(Description\) 给定一天24h 每小时需要的员工数量Ri,有n个员工,已知每个员工开始工作的时间ti(ti∈[0,23]),每个员工会连续工作8h. 问能否满足一天的需求.若 ...

  3. POJ 3159 Candies 还是差分约束(栈的SPFA)

    http://poj.org/problem?id=3159 题目大意: n个小朋友分糖果,你要满足他们的要求(a b x 意思为b不能超过a x个糖果)并且编号1和n的糖果差距要最大. 思路: 嗯, ...

  4. [ACM] 最短路算法整理(bellman_ford , SPFA , floyed , dijkstra 思想,步骤及模板)

    以杭电2544题目为例 最短路 Problem Description 在每年的校赛里,全部进入决赛的同学都会获得一件非常美丽的t-shirt. 可是每当我们的工作人员把上百件的衣服从商店运回到赛场的 ...

  5. POJ 3159 Candies 解题报告(差分约束 Dijkstra+优先队列 SPFA+栈)

    原题地址:http://poj.org/problem?id=3159 题意大概是班长发糖果,班里面有不良风气,A希望B的糖果不比自己多C个.班长要满足小朋友的需求,而且要让自己的糖果比snoopy的 ...

  6. 图论最短路径算法总结(Bellman-Ford + SPFA + DAGSP + Dijkstra + Floyd-Warshall)

    这里感谢百度文库,百度百科,维基百科,还有算法导论的作者以及他的小伙伴们...... 最短路是现实生活中很常见的一个问题,之前练习了很多BFS的题目,BFS可以暴力解决很多最短路的问题,但是他有一定的 ...

  7. 图论算法》关于SPFA和Dijkstra算法的两三事

    本来我是想把这两个算法分开写描述的,但是SPFA其实就是Dijkstra的稀疏图优化,所以其实代码差不多,所以就放在一起写了. 因为SPFA是Dijkstra的优化,所以我想来讲讲Dijkstra. ...

  8. luogu P3371 & P4779 单源最短路径spfa & 最大堆优化Dijkstra算法

    P3371 [模板]单源最短路径(弱化版) 题目背景 本题测试数据为随机数据,在考试中可能会出现构造数据让SPFA不通过,如有需要请移步 P4779. 题目描述 如题,给出一个有向图,请输出从某一点出 ...

  9. 关于SPFA Bellman-Ford Dijkstra Floyd BFS最短路的共同点与区别

    关于模板什么的还有算法的具体介绍 戳我 这里我们只做所有最短路的具体分析. 那么同是求解最短路,这些算法到底有什么区别和联系: 对于BFS来说,他没有松弛操作,他的理论思想是从每一点做树形便利,那么时 ...

随机推荐

  1. python序列化模块的速度比较

    # -*- coding: utf-8 -*- # @Time : 2019-04-01 17:41 # @Author : cxa # @File : dictest.py # @Software: ...

  2. Windows PowerShell 入門(4)-変数と演算子

    Windows PowerShellにおける変数と演算子の使用方法について学びます.今回は代表的な演算子として.算術演算子.代入演算子.論理演算子.比較演算子.範囲演算子.置換演算子.ビット演算子.型 ...

  3. cu命令

    选项: -b:仅显示行中指定直接范围的内容: -c:仅显示行中指定范围的字符: -d:指定字段的分隔符,默认的字段分隔符为“TAB”: -f:显示指定字段的内容: -n:与“-b”选项连用,不分割多字 ...

  4. hibernate框架学习之数据查询(QBC)

    lQBC(Query By Criteria)是一种Hibernate中使用面向对象的格式进行查询的计数 lQBC查询方式步骤 •获取Session对象 •初始化Criteria对象(使用Sessio ...

  5. Tickets HDU - 1260 水DP

    HDU - 1260 现在有n个人要买电影票,如果知道每个人单独买票花费的时间, 还有和前一个人一起买花费的时间,问最少花多长时间可以全部买完票. 直接dp就行,注意下输出和初始化 每次从dp[i-1 ...

  6. <TCP/IP>记一次关于IP地址和MAC物理地址的思考

    是的,从3月6日第一次上计算机网络课起,我还是今天第一次对这本书里讲的知识点有了自己的疑问..之前看书就是 嗯嗯这好像很有道理,嗯嗯也许再多看几章就知道它在讲什么了.. 不过今天已经自学到了网络层了, ...

  7. Docker从0开始之部署一套2048小游戏

    本文记录一下在docker部署一套2048小游戏的过程,在娱乐中熟悉docker的应用部署.docker 安装不在本文讲述之中,参考我的其它博客. 1.获取image镜像. 方法一:daocloud. ...

  8. MSSQL—行转列

    行转列,是SQL中经常会遇到的一个问题,并且分为静态转换和动态转换,所谓静态转换即在转换的行数已知或固定:动态转换则为转换的行数不固定. 转换的方法一般采用case when语句或pivot(MSSQ ...

  9. 转载:UML学习(一)-----用例图 (silent)

    原文:http://www.cnblogs.com/silent2012/archive/2011/09/07/2169518.html 1.什么是用例图 用例图源于Jacobson的OOSE方法,用 ...

  10. Light OJ 1012

    经典搜索水题...... #include<bits/stdc++.h> using namespace std; const int maxn = 20 + 13; const int ...