最短路+差分约束

dijikstra不能求最长路

spfa求最短路可以判断负环,求最长路可以判断正环

差分约束系统详解(解释为什么要设置超级源点,判断连通性

差分约束介绍

POJ 1364 King(非连通图的差分约束,经典好题)

再卖菜csdn

SPFA(shortest path fast algorithm)及差分约束

参考链接(夜深人静写算法)

spfa求最短路/最长路数据结构及代码

//SPFA求最短路+记录路径+判断负圈
struct edge{
  int from,to,cost;  
};

vector<vector<edge> > graph;
bool inqueue[MAX_N];   //记录该节点是否在队列中
int visitcount[MAX_N];//记录节点的入队次数,判断负权圈时要用到,若某节点的该次数>n则存在负权圈
int d[MAX_N]; //记录从源点到对应点的当前最短/最长路径
int father[MAX_N];  //father[i]表示i节点最短/最长路径上的上一个节点

void SPFA(int ss)  //表示从ss节点开始求
{
    for(int i=0;i<MAX_N;i++)
    {
        d[i]=(i==s)?0:INF;       //***************求最长路要在这里改
        inqueue[i]=(i==s);
        visitcount[i]=0;
    }
    queue<pair<int,int>> Q;   //可以简化为queue<int> Q,只将节点序号放入队列中
    Q.push(make_pair(d[ss],ss));
    while(!Q.empty())
    {
        pair<int,int> tem=Q.top();
        int dist=tem.first;
        int node=tem.second;
        Q.pop();
        inqueue[node]=false;
        //visitcount[]的更新在出队时进行,保证每个队列中的节点都能更新visitcount
        if(visitcount[node]++>MAX_N)//有负权圈,直接退出算法
            return; 
        for(int j=0;j<graph[node].size();j++)
        {
            edge e=graph[node][j];
            int from=e.from,to=e.to,cost=e.cost;
            if(cost+d[from]<d[to])  //********************求最长路要在这里改
            {
                father[to]=from;  //说明当前到to的最短路的上一个节点是from
                d[to]=cost+d[from];
//若更新的节点没在队列中,则入队。因为队列中只有inqueue这个入队与否的标志,保证队列中只有最多一个该节点
                if(!inqueue[to])  
                {
                    Q.push(make_pair(d[to],to));
                	inqueue[to]=true;
                }
                
            }
        }
    }
}
对于spfa,它只让该轮被松弛过的节点入队,该轮没有松驰过的节点,则下轮没有松弛其的必要(因为当前到该节点的路径值一定不是最短的,因此也没必要用这个非最短路去松弛其他节点)

差分约束定义

如若一个系统由n个变量和m个不等式组成,并且这m个不等式对应的系数矩阵中每一行有且仅有一个1和-1,其它的都为0,这样的系统称为**差分约束( difference constraints )**系统。之所以能用spfa处理差分约束问题,是因为能从差分约束的不等式组中建立图的模型。

差分约束的经典应用:

1.线性约束

**【例题】**N个人编号为1-N,并且按照编号顺序排成一条直线,任何两个人的位置不重合,然后给定一些约束条件。

​ X(X <= 100000)组约束Ax Bx Cx(1 <= Ax < Bx <= N),表示Ax和Bx的距离不能大于Cx。

​ Y(X <= 100000)组约束Ay By Cy(1 <= Ay < By <= N),表示Ay和By的距离不能小于Cy。

​ 如果这样的排列存在,输出1-N这两个人的最长可能距离,如果不存在,输出-1,如果无限长输出-2。**

2.区间约束

**【例题】**给定n(n <= 50000)个整点闭区间和这个区间中至少有多少整点需要被选中,每个区间的范围为[ai, bi],并且至少有ci个点需要被选中,其中0 <= ai <= bi <= 50000,问[0, 50000]至少需要有多少点被选中。

​ 例如3 6 2 表示[3, 6]这个区间至少需要选择2个点,可以是3,4也可以是4,6(总情况有 C(4, 2)种 )。**

3、未知条件约束(待看

未知条件约束是指在不等式的右边不一定是个常数,可能是个未知数,可以通过枚举这个未知数,然后对不等式转化成差分约束进行求解。

​ **【例题】**在一家超市里,每个时刻都需要有营业员看管,R(i) (0 <= i < 24)表示从i时刻开始到i+1时刻结束需要的营业员的数目,现在有N(N <= 1000)个申请人申请这项工作,并且每个申请者都有一个起始工作时间 ti,如果第i个申请者被录用,那么他会连续工作8小时。

现在要求选择一些申请者进行录用,使得任何一个时刻i,营业员数目都能大于等于R(i)。

CCF-2018-9 再卖菜

是一道隐含的差分约束题目,解释如下

设ai是第一天i商店的价格,bi为第二天i商店的价格

bellman-ford

struct edge { int from,to,cost;}

edge es[MAX_N];   //该算法以 边集 为数据基础  

int d[MAX_N];  //记录最短距离
int count[MAX_N];  //记录节点的松弛次数
int V,E;  //节点数,边数

bool shortest_path(int s){
    memset(count,o,sizeof(count));
    for(int i=0;i<V;i++) d[i]=INF;
    d[s]=0;
    while(true)   //让节点一直更新,实际上若能找到最短路径,循环不会超过n次.for(int i=0;i<n;i++)
    {
        bool update=false;
        for(int i=0;i<E;i++)
        {
            edge e=es[i];
            if(d[e.from]+e.cost<d[e.to])
            {
                d[e.to]=d[e.from]+e.cost;
                update=true;
                if(count[e.to]++>=MAX_N)  //N个节点,每个节点最多松弛N-1次
                    return false;  //有负圈
            }
        }
        if(!update)  //不再更新说明已经找到所有最短路径
            return true;
    }
}//bellman-ford 的改进方法就是spfa

Dijkstra+优先队列

//使用矩阵dijkstra的复杂度为O(V^2),使用邻接表+优先队列复杂度为O(V*logE)
struct edge{
    int from, to cost;
}

typedef pair<int,int> P;  //first为最短距离,second为节点编号
bool operator<(const P& p1,const P& p2)
{
    return p1.first>=p2.first;
}

int V;
vector<vector<edge> > graph(MAX_N);
int d[MAX_N];

void dijkstra(int s){
    priority<P> pq;
   	memset(d,127,sizeof(d));  //INF
    d[s]=0;
    pq.push(make_pair(0,s));
    
    whlie(!pq.empty())
    {
        P tep=pq.top(); pq.pop();
        int v=tep.second;
        //队首元素是队列中最小的,但若不能松弛该节点,此时该队首元素没用(对于已经找到最小路径的节点,即使到它的另外一条路现在是队列中的最小值,也没用)
        if(d[v]<tep.first) continue; 
        for(int i=0;i<graph[v].size();i++)
        {
            edge e=graph[v][i];
            if(d[e.to]>d[e.from]+e.cost)
            {
                d[e.to]=d[e.from]+e.cost;   //d[]存的就是临时最短路径
                pq.push(make_pair(d[e.to],e.to));
                //放入队列中的都是由当前最短距离 增加一条边的联系得来的
            }
        }
    }
}

dij+堆链接

priority_queue默认是最大的元素在队头(大顶堆)

自定义排序方式的重载: 链接 sort运算符重载

//类(结构体)的方式
struct cmp{
  bool operator ()(const int& a,const int &b){
      return a>b;
  }  
};
//在类中重载运算符<(不能在类外直接重载,除非加友元(比较麻烦所以不推荐)
struct Person{
    int id;
    bool operator <(const Person& b){
    return this->b->id;
	}
};

//定义函数
bool compare(const int& a,const int& b){
    return a>b;
}
//或者 使用less<int>或者greater<int>对基本类型排序。

//对于sort推荐用函数compare方式,对于priority_queue一般用结构体方式
sort(arr,arr+n;compare);
sort(arr,arr+n,cmp());  //注意sort用结构体方式要cmp()
sort(arr,arr+n,greater<int>());  //注意要加(),是递减排序

priority_queue<int,vector<int>,cmp>;  //pq用结构体方式直接cmp
priority_queue<int,vector<int>,greater<int>> //greater<>不用加(),最小堆
    
//可以发现相同的比较函数用于sort和pq是“相反的顺序”(递增-最大堆,递减-最小堆)

FLOYD算法

Floyd-Warshall算法的原理是动态规划

要将节点先编号1~n, D i , j , k D_{i,j,k} Di,j,k表示i->j 只经过1~k节点(中的若干任意节点)得到的最短路径值。

再分k节点是否在这条最短路上,有两种情况,根据这两种情况推出 D i , j , k − 1 D_{i,j,k-1} Di,j,k1 的关系式,之后就可以用DP

时间复杂度为O(n3),空间复杂度为O(n2)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值