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

转自: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. 在Linux下误删文件后恢复【转】

    针对日常维护操作,难免会出现文件误删除的操作.大家熟知linux文件系统不同win有回收站,删除后的文件可以到垃圾箱寻回,要知道linux文件修复比较费劲,网络上面的文档也是五花八门.所以本次研究一种 ...

  2. NUMA的关闭方法【转】

    Centos 6 在/etc/grub.conf    在kernel 添加numa=off 就行了 一.检查OS是否开启NUMA # numactl --hardware available: 1 ...

  3. mac使用技巧之截图

    1.选定区域截图 command+shift+3 选定区域按住control,可以直接截图到剪切板, 按option可以进行比例缩放. 按住shift可以选定区域 按住空格键可以进入窗口截图模式. 2 ...

  4. python标准库之argparse

    argparse的使用 argparse 是 Python 内置的一个用于命令项选项与参数解析的模块,通过在程序中定义好我们需要的参数,argparse 将会从 sys.argv 中解析出这些参数,并 ...

  5. linux学习之硬盘的存储原理和内部架构

    原文地址:https://blog.csdn.net/tanggao1314/article/details/52074735 首先,让我们看一下硬盘的发展史: 1956年9月13日,IBM的IBM ...

  6. node学习第一天:nvm使用

    nvm是什么? 学习node,首先要安装node的环境,nvm是一款工具,使用这款工具可以很方便的下载所需版本的node文件以及npm,十分的方便. nvm下载: nvm下载链接 注:下载文件名为 n ...

  7. Kali Linux2018 上安装open-vm-tools实现虚拟机交互

    最新的kali linux2018已经不再支持原有的vmwaretools,即使安装了也不能实现主机与客户机之间的交互(比如从主机复制文件到客户机).安装open-vm-tools替代vm tools ...

  8. Eclipse中三种设置编码格式的方法

    转自:https://blog.csdn.net/rainy_black_dog/article/details/52403735 很早以前听过一位老师说过:咱们中国人不管学习哪种编程语言,总会遇到乱 ...

  9. Android启动模式之singleinstance的坑

    前言 在实际应用中,使用singleinstance启动模式时,会遇到一些奇奇怪怪的问题.Android有四种启动模式,分别是standard,singleTop,singleTask,singleI ...

  10. aview安装和使用

    一.安装aalibwget https://sourceforge.net/projects/aa-project/files/latest/download?source=files --no-ch ...