1有n个未知数,其中有m个约束条件,形如x1-x2<=c之类的,问是否存在满足所有约束条件的情况。
2对于条件x1-x2<=c,可以转换成x1<=c+x2,此时可以建一个x2连向x1边权为c的边,因为这个公式和最短路中的松弛操作很像。假设有两个式子,x1<=c+x2和x1<=c+x3,分别建一条x2到x1和x3到x1的边权为c的边,为了满足条件,x1肯定越小越好,如果x1出度为0,那么x1怎么小都没影响,而x1有指向其他点的边,那么x1尽量要大一点,最短路求出的就是其最大值。
3创造一个超级源点,连接所有的点,边权为0,跑spfa判断是否有负环,如果有负环,说明不满足约束条件
代码实现:
根据模板题:小 K 的农场 - 洛谷
1数据存储:
const int maxn=1e4+10;
const int inf=0x3f3f3f3f;
int n,m;
struct edge{
int to,w;
};
vectorvv[maxn];
void add(int u,int v,int w)
{
vv[u].push_back({v,w});
}
bool in[maxn];//标记是否入队列
int cnt[maxn];//spfa记录到达该点经过的路径
int dis[maxn];//最短路
2spfa判断负环
bool spfa(int s)
{
for(int i=1;i<=n;i++)
dis[i]=inf;
dis[s]=0;
queueq;
q.push(s);
in[s]=1;
while(!q.empty())
{
int u=q.front();
q.pop();
in[u]=0;
for(int i=0;idis[u]+w)
{
dis[to]=dis[u]+w;
cnt[to]=cnt[u]+1;
if(cnt[to]>=n+1)
return 0;
if(!in[to])
{
q.push(to);
in[to]=1;
}
}
}
}
return 1;
}
3主函数(建边)
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int op,u,v,c;
cin>>op;
if(op==1)//u-c>=v 建一条u到v边权为-c的边
{
cin>>u>>v>>c;
add(u,v,-c);
}
else if(op==2)//u<=v+c 建一条v到u权为c的边
{
cin>>u>>v>>c;
add(v,u,c);
}
else//u<=v+0 u+0>=v 分别建一条u到v和v到u边权都为0的边
{
cin>>u>>v;
add(u,v,0);
add(v,u,0);
}
}
for(int i=1;i<=n;i++)
add(0,i,0);
if(spfa(0))
cout<<"Yes\n";
else
cout<<"No\n";
return 0;
}
4完整代码
#include
#define ll long long
using namespace std;
const int maxn=1e4+10;
const int inf=0x3f3f3f3f;
int n,m;
struct edge{
int to,w;
};
vectorvv[maxn];
void add(int u,int v,int w)
{
vv[u].push_back({v,w});
}
bool in[maxn];//标记是否入队列
int cnt[maxn];//spfa记录到达该点经过的路径
int dis[maxn];//最短路
bool spfa(int s)
{
for(int i=1;i<=n;i++)
dis[i]=inf;
dis[s]=0;
queueq;
q.push(s);
in[s]=1;
while(!q.empty())
{
int u=q.front();
q.pop();
in[u]=0;
for(int i=0;idis[u]+w)
{
dis[to]=dis[u]+w;
cnt[to]=cnt[u]+1;
if(cnt[to]>=n+1)
return 0;
if(!in[to])
{
q.push(to);
in[to]=1;
}
}
}
}
return 1;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int op,u,v,c;
cin>>op;
if(op==1)//u-c>=v 建一条u到v边权为-c的边
{
cin>>u>>v>>c;
add(u,v,-c);
}
else if(op==2)//u<=v+c 建一条v到u权为c的边
{
cin>>u>>v>>c;
add(v,u,c);
}
else//u<=v+0 u+0>=v 分别建一条u到v和v到u边权都为0的边
{
cin>>u>>v;
add(u,v,0);
add(v,u,0);
}
}
for(int i=1;i<=n;i++)
add(0,i,0);
if(spfa(0))
cout<<"Yes\n";
else
cout<<"No\n";
return 0;
}