满足多组二元差的不等式关系x1...xn,求是否有解,最小解,最大解。求解一般用spfa(因为有负权)。对于最小解一般用最长路,最大解一般用最短路。对于小于号和大于号,可以通过减1来变成小于等于或者大于等于。不满足需要的不等式的时候可以给k添个负号。
对于x-y<=k,k是边权,x,y是点,可以转化成x<=y+k。我们令有k的一边表示起始点,y+k就是起始点的距离加上起始点到终点的边权,那么x<=y+k就是让终点的dis要小于y+k,也就是说松弛条件就是if(dis【x】>dis【y】+w)就更新dis【x】,使得x<=y+k。可以发现这是最短路的松弛。由于我们每次都是令x取得x<=y+k满足条件的最大值(y+k),所以最终结果就是满足条件的最大值。
对于x-y>=k,可以转化成x>=y+k,也就是起点的距离加上边权要小于终点的距离,松弛条件就是if(dis【x】<dis【y】+k)就更新dis【x】,这样使得x>=y+k。可以发现这是最长路的松弛。由于我们每次都是令x取得x>=y+k满足条件的最小值(y+k),所以最终结果就是满足条件的最小值。
由于题目要求最小值,我们要用最长路算法,那么关系就要找x+y>=k。对于条件2,A要比B小,也就是A<B,A+1<=B,也就是B>=A+1,那么起点就是A,边权就是1。对于条件4,A>B,A>=B+1,那么起点就是B,边权就是1。因为条件1是相等,那么我们只需要用两条不等式的交集来表示范围即可,A>=B+0,B>=A+0,这两条不等式的交集条件就是A=B。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
queue<int>q;
struct edge
{
int to,from,v;
}e[800010];
int n,dis[400010],head[400010],cnt=1,bz[400010],k;
bool f[400010];
void insert(int x,int y,int v)
{
e[++cnt].to=y;e[cnt].from=head[x];e[cnt].v=v;head[x]=cnt;
}
int main()
{
scanf("%d%d",&n,&k);
for (int i=1;i<=k;i++)
{
int x,a,b;
scanf("%d%d%d",&x,&a,&b);
if (x==1)
{
insert(a,b,0);
insert(b,a,0);
} else
if (x==2)
{
if (a==b)
{
printf("-1");
return 0;
}
insert(a,b,1);
} else
if (x==3) insert(b,a,0); else
if (x==4)
{
if (a==b)
{
printf("-1");
return 0;
}
insert(b,a,1);
} else
if (x==5) insert(a,b,0);
}
for (int i=n;i>=1;i--)
insert(0,i,1);
memset(dis,0,sizeof(dis));
memset(f,false,sizeof(f));
f[0]=true;
q.push(0);
while (!q.empty())
{
int u=q.front();
q.pop();
f[u]=false;
if (bz[u]==n-1)
{
printf("-1");
return 0;
}
bz[u]++;
for (int i=head[u];i;i=e[i].from)
if (dis[e[i].to]<dis[u]+e[i].v)
{
dis[e[i].to]=dis[u]+e[i].v;
if (!f[e[i].to])
{
f[e[i].to]=true;
q.push(e[i].to);
}
}
}
long long ans=0;
for (int i=1;i<=n;i++)
ans+=dis[i];
printf("%lld",ans);
return 0;
}