差分约束
__Green_tick__ · · 算法·理论
差分约束
前置知识
-
- 多元一次不等式组
经典例题
- P5960 【模板】差分约束
- P1993 小 K 的农场
算法作用
求多元一次不等式组的一组或多组解。
如下,是一个多元一次不等式组:
也是差分约束的一般形式。
算法定义
差分约束系统 是一种特殊的
差分约束系统中的每个约束条件
注意到,如果
算法步骤
设
算法性质
一般使用 Bellman–Ford 或队列优化的 Bellman–Ford(俗称 SPFA,在某些随机图跑得很快)判断图中是否存在负环,最坏时间复杂度为
P5960 【模板】差分约束
正确代码
#include<bits/stdc++.h>
using namespace std;
/*Optimization series
#pragma GCC/G++ optimize(1)
#pragma GCC/G++ optimize(2)
#pragma GCC/G++ optimize(3,"OSumast","inline")
*/
const long long MaxN=5e3+10;
long long dis[MaxN];
long long num[MaxN];
bool vis[MaxN];
long long elast[MaxN];
queue<long long> que;
long long n,m,cnt;
struct Edge{
long long next;
long long len;
long long to;
}edge[MaxN*2];
void Add_Edge(long long u,long long v,long long y){//加边:u -> v 权值为 y
cnt++;
edge[cnt].to=v;
edge[cnt].len=y;
edge[cnt].next=elast[u];
elast[u]=cnt;
return;
}
bool SPFA(long long start){
for(long long i=1;i<=n;i++){
dis[i]=INT_MAX;
vis[i]=false;
}
dis[start]=0;
vis[start]=true;
que.push(start);
num[start]++;
while(que.size()){
long long u=que.front();
que.pop();
vis[u]=false;
for(long long i=elast[u];i;i=edge[i].next){
if(dis[edge[i].to]>dis[u]+edge[i].len){
dis[edge[i].to]=dis[u]+edge[i].len;
if(num[edge[i].to]>=n){
return false;//出现负环即无解
}
if(vis[edge[i].to]==false){
que.push(edge[i].to);
num[edge[i].to]++;
vis[edge[i].to]=true;
}
}
}
}
return true;
}
int main(){
cin>>n>>m;
for(long long i=1;i<=n;i++){
Add_Edge(0,i,0);//建立虚拟源点及其边
}
for(long long i=1;i<=m;i++){
long long u,v,y;
cin>>u>>v>>y;
Add_Edge(u,v,y);//建边
}
if(!SPFA(0)) cout<<"NO"<<endl;//无解(出现负环)
else{
for(long long i=1;i<=n;i++){
cout<<dis[i]<<" ";
}
cout<<endl;
}
return 0;
}
例题 P1993 小 K 的农场
题目大意
求解差分约束系统,有 m 条约束条件,每条都为形如
思路讲解
令
先读入一个
按照题意:
- 农场
a 比农场b 至少多种植了c 个单位的作物;- 农场
a 比农场b 至多多种植了c 个单位的作物;- 农场
a 与农场b 种植的作物数一样多。
当
- 加入边
u 到v 的边权值为-y ;
当
- 加入边
v 到u 的边权值为y ;
当
- 加入边
u 到v 的边权值为0 ; - 加入边
v 到u 的边权值为0 。
则有如下代码:
if(opt==1){
long long u,v,y;
cin>>u>>v>>y;
Add_Edge(u,v,-y);
}else if(opt==2){
long long u,v,y;
cin>>u>>v>>y;
Add_Edge(v,u,y);
}else{
long long u,v;
cin>>u>>v;
Add_Edge(v,u,0);
Add_Edge(u,v,0);
}
正确代码
#include<bits/stdc++.h>
using namespace std;
/*Optimization series
#pragma GCC/G++ optimize(1)
#pragma GCC/G++ optimize(2)
#pragma GCC/G++ optimize(3,"OSumast","inline")
*/
const long long MaxN=5e3+10;
long long dis[MaxN];
long long num[MaxN];
bool vis[MaxN];
long long elast[MaxN];
queue<long long> que;
long long n,m,cnt;
struct Edge{
long long next;
long long len;
long long to;
}edge[MaxN*2];
void Add_Edge(long long u,long long v,long long y){
cnt++;
edge[cnt].to=v;
edge[cnt].len=y;
edge[cnt].next=elast[u];
elast[u]=cnt;
return;
}
bool SPFA(long long start){
for(long long i=1;i<=n;i++){
dis[i]=INT_MAX;
vis[i]=false;
}
dis[start]=0;
vis[start]=true;
que.push(start);
num[start]++;
while(que.size()){
long long u=que.front();
que.pop();
vis[u]=false;
for(long long i=elast[u];i;i=edge[i].next){
if(dis[edge[i].to]>dis[u]+edge[i].len){
dis[edge[i].to]=dis[u]+edge[i].len;
if(num[edge[i].to]>=n){
return false;
}
if(vis[edge[i].to]==false){
que.push(edge[i].to);
num[edge[i].to]++;
vis[edge[i].to]=true;
}
}
}
}
return true;
}
int main(){
cin>>n>>m;
for(long long i=1;i<=n;i++){
Add_Edge(0,i,0);
}
for(long long i=1;i<=m;i++){
long long opt;
cin>>opt;
if(opt==1){
long long u,v,y;
cin>>u>>v>>y;
Add_Edge(u,v,-y);
}else if(opt==2){
long long u,v,y;
cin>>u>>v>>y;
Add_Edge(v,u,y);
}else{
long long u,v;
cin>>u>>v;
Add_Edge(v,u,0);
Add_Edge(u,v,0);
}
}
if(!SPFA(0)) cout<<"No"<<endl;
else cout<<"Yes"<<endl;
return 0;
}