浅谈差分约束和同余最短路
chenhanzheapple · · 算法·理论
差分约束
定义
定义 差分约束系统 为一组形如
的
差分约束是用来求解该不等式的的一种图论建模算法。
过程
发现差分约束系统中的每个约束条件
最后求得的解为
注意到该差分约束系统可能无解,因此在图中判断是否存在负环即可。
该算法求解的是差分约束系统的一组特解,想要求出通解可以将所有
一般来说,差分约束建模得出的图中有负权边,因此一般使用 SPFA 算法进行求解,时间复杂度为
证明
由于是进行最短路操作,因此对于任意
代码实现
以洛谷 P5960 为例:
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
int n,m;
int dis[5005],cnt[5005];
bool vis[5005];
struct node{
int x,w;
};
vector<node> v[5005];
bool spfa(int s){
memset(dis,0x3f,sizeof(dis));
queue<int> que;
que.push(s);
dis[s] = 0;
vis[s] = 1;
while(!que.empty()){
int x = que.front();
que.pop();
vis[x] = 0;
for(auto y:v[x]){
if(dis[x]+y.w<dis[y.x]){
dis[y.x] = dis[x]+y.w;
if(!vis[y.x]){
que.push(y.x);
vis[y.x] = 1;
if(++cnt[y.x]>=n){
return 1;
}
}
}
}
}
return 0;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for(int i=1;i<=m;i++){
int x,y,w;
cin >> x >> y >> w;
v[y].push_back((node){x,w});
}
n++;
for(int i=1;i<n;i++){
v[n].push_back((node){i,0});
}
if(spfa(n)){
cout << "NO";
}
else{
for(int i=1;i<n;i++){
cout << dis[i] << " ";
}
}
return 0;
}
同余最短路
定义
同余最短路算法是用于求解形如“给出
过程
首先,我们可以分别求出
现在考虑如何求解
考虑将原式变形。在
考虑使用动态规划解决该问题。我们用
因此我们有如下转移方程:
相当于在
发现这相当于在图中建
由于对于任意的
实际代码实现时并不需要把图建出来。代码中要特殊处理
由于图中有
代码
以洛谷 P2371 为例。
SPFA 写法
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
int n,l,r;
int a[15],dis[500005];
bool vis[500005];
int calc(int h){
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
queue<int> que;
dis[0] = 0;
vis[0] = 1;
que.push(0);
while(!que.empty()){
int x = que.front();
que.pop();
vis[x] = 0;
for(int i=2;i<=n;i++){
int y = (x+a[i])%a[1];
if(dis[x]+a[i]<dis[y]){
dis[y] = dis[x]+a[i];
if(!vis[y]){
vis[y] = 1;
que.push(y);
}
}
}
}
int res = 0;
for(int i=0;i<a[1];i++){
if(h>=dis[i]){
res+=(h-dis[i])/a[1]+1;
}
}
return res;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> l >> r;
int m = 0;
for(int i=1;i<=n;i++){
int x;
cin >> x;
if(x){
a[++m] = x;
}
}
n = m;
if(!n){
cout << 0;
return 0;
}
sort(a+1,a+n+1);
cout << calc(r)-calc(l-1);
return 0;
}
Dijkstra 写法
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
int n,l,r;
int a[15],dis[500005];
bool vis[500005];
struct node{
int x,w;
bool operator < (const node &b) const{
return b.w<w;
}
};
int calc(int h){
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
priority_queue<node> que;
dis[0] = 0;
que.push((node){0,0});
while(!que.empty()){
node x = que.top();
que.pop();
if(vis[x.x]){
continue;
}
vis[x.x] = 1;
for(int i=2;i<=n;i++){
int y = (x.x+a[i])%a[1];
if(dis[x.x]+a[i]<dis[y]){
dis[y] = dis[x.x]+a[i];
que.push((node){y,dis[y]});
}
}
}
int res = 0;
for(int i=0;i<a[1];i++){
if(h>=dis[i]){
res+=(h-dis[i])/a[1]+1;
}
}
return res;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> l >> r;
int m = 0;
for(int i=1;i<=n;i++){
int x;
cin >> x;
if(x){
a[++m] = x;
}
}
n = m;
if(!n){
cout << 0;
return 0;
}
sort(a+1,a+n+1);
cout << calc(r)-calc(l-1);
return 0;
}