本题选自洛谷训练场。。。传送门,点击!
First.
思路分析
Second.
此处介绍两种方法:
No.1:最短路
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const ll maxn = 50010;
ll k,n,m;
ll in[maxn];
ll dis[maxn];
ll head[maxn];
bool vis[maxn];
struct edge
{
ll v,w,nxt;
edge(){}
edge(ll _v,ll _w,ll _nxt)
{
v=_v;
w=_w;
nxt=_nxt;
}
}e[maxn];
void init()
{
memset(head,-1,sizeof(head));
memset(dis,maxn*100,sizeof(dis));
k=0;
}
void add(ll u,ll v,ll w)
{
e[k]=edge(v,w,head[u]);
head[u]=k++;
}
bool spfa(ll u)
{
queue q;
q.push(u);
vis[u]=true;
dis[u]=0;
in[u]=1;
while(!q.empty())
{
u=q.front();
q.pop();
vis[u]=false;
for(ll i=head[u];~i;i=e[i].nxt)
{
ll v=e[i].v;
ll w=e[i].w;
if(dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
if(!vis[v])
{
q.push(v);
vis[v]=true;
in[v]++;
if(in[v]>20)
return false;
}
}
}
}
return true;
}
int main()
{
// freopen("farm.in","r",stdin);
// freopen("farm.out","w",stdout);
ios::sync_with_stdio(false);
init();
ll mod,a,b,c;
cin>>n>>m;
for(ll i=1;i<=n;i++)
add(0,i,0);
while(m--)
{
cin>>mod;
switch(mod)
{
case 1:{
cin>>a>>b>>c;//a-b>=c->a>=b+c->0>=b-a+c->-c>=b-a
add(a,b,-c);
};break;
case 2:{
cin>>a>>b>>c;//a-b<=c
add(b,a,c);
};break;
case 3:{
cin>>a>>b;//a==b
add(a,b,0);
add(b,a,0);
};break;
}
}
if(spfa(0))
cout<<"Yes"<
NO.2:最长路
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const ll maxn = 50010;
ll k,n,m;
ll in[maxn];
ll dis[maxn];
ll head[maxn];
bool vis[maxn];
struct edge
{
ll v,w,nxt;
edge(){}
edge(ll _v,ll _w,ll _nxt)
{
v=_v;
w=_w;
nxt=_nxt;
}
}e[maxn];
void init()
{
memset(head,-1,sizeof(head));
memset(dis,-maxn*100,sizeof(dis));
k=0;
}
void add(ll u,ll v,ll w)
{
e[k]=edge(v,w,head[u]);
head[u]=k++;
}
bool spfa(ll u)
{
queue q;
q.push(u);
vis[u]=true;
dis[u]=0;
in[u]=1;
while(!q.empty())
{
u=q.front();
q.pop();
vis[u]=false;
for(ll i=head[u];~i;i=e[i].nxt)
{
ll v=e[i].v;
ll w=e[i].w;
if(dis[v]20)
return false;
}
}
}
}
return true;
}
int main()
{
// freopen("farm.in","r",stdin);
// freopen("farm.out","w",stdout);
ios::sync_with_stdio(false);
init();
ll mod,a,b,c;
cin>>n>>m;
for(ll i=1;i<=n;i++)
add(0,i,0);
while(m--)
{
cin>>mod;
switch(mod)
{
case 1:{
cin>>a>>b>>c;//a-b>=c->a>=b+c->0>=b-a+c->-c>=b-a
add(a,b,-c);
};break;
case 2:{
cin>>a>>b>>c;//a-b<=c
add(b,a,c);
};break;
case 3:{
cin>>a>>b;//a==b
add(a,b,0);
add(b,a,0);
};break;
}
}
if(spfa(0))
cout<<"Yes"<
Third.
此题为什么会有最短路与最长路的算法但是最后却都能得到正确的答案?
这是因为差分约束系统中主体程序为SPFA,是为一种可以判断正环和负环的最短(长)路算法,当我们反向亦或是正向建图建图时,如果图中存在环,则对于最短路存在负环,而对于最长路存在正环。
所以,不论是哪种算法都可以得到正确的解;
P.S.我在SPFA中将入队次数改为了20次而非n+1次是因为有可能会超时,但还有一种更保险的方式是计算出每一个点的度数,来进行判断,不在此处赘述;
Fourth.
共勉吧,诸君!