最短路+差分约束

dijikstra不能求最长路

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

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

差分约束介绍

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

再卖菜csdn

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

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

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

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

vector > 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> Q;   //可以简化为queue Q,只将节点序号放入队列中
    Q.push(make_pair(d[ss],ss));
    while(!Q.empty())
    {
        pair 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

差分约束定义

如若一个系统由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=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 P;  //first为最短距离,second为节点编号
bool operator<(const P& p1,const P& p2)
{
    return p1.first>=p2.first;
}

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

void dijkstra(int s){
    priority

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]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或者greater对基本类型排序。

//对于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)

你可能感兴趣的:(算法)