【Luogu 1993】差分约束系统问题——小K的农场

Luogu P1993
前置知识:最短路径相关算法

如果一个系统由n个变量和m个约束条件组成,形成m个形如ai-aj≤k的不等式(i,j∈[1,n],k为常数),则称其为差分约束系统。

显然题目中给出的信息可以构成一个差分约束系统,虽然不等号的方向不统一,但是我们可以利用不等式的性质进行变换,将其全部统一成小于等于号(更换成小于等于号的原因是需要用到三角不等式)。
众所周知,最短路径算法中有一条三角不等式,即:
\[cost[x]<=cost[y]+val[y][x]\]
通过移项就可以变化成:
\[cost[x]-cost[y]<=val[y][x]\]
这与题目中给出的条件形式上是完全一致的。
那么我们就找到了解差分约束系统的方法:把形如\(a-b<=c\)的不等式视为在一个有向图中从\(b\)节点连一条权值为\(c\)的边到\(a\)节点,通过单源最短路径算法就可以求解。
那么如果图不连通怎么办?当然是选择加入一个超级点,向每一个节点连接一条权值为0的边。(其实好像也可以对每一个连通块其中的一个点跑一次SPFA)

//类BFS的SPFA会TLE,开O2可过
//建议使用类DFS的SPFA,然而我并不会(判负环时DFS-SPFA比较高效)
#include
#include
using namespace std;
struct data
{
    int to,next,val;
}e[50005];
int cost[50005],flag,head[50005],n,m,a,b,c,cnt,cnt1[50005];
queue que;
bool vis[50005];
void add(int u,int v,int w)
{
    e[++cnt].to=v;
    e[cnt].next=head[u];
    e[cnt].val=w;
    head[u]=cnt;
}
void First(int s)
{
    for (int i=1;i<=n;i++) cost[i]=0x3f3f3f3f;
    cost[s]=0;
}
bool SPFA(int s)
{
    que.push(s);
    vis[s]=true;
    while (!que.empty())
    {
        int now=que.front();
        que.pop();
        vis[now]=false;
        for (int i=head[now];i;i=e[i].next)
        {
            if (cost[e[i].to]>cost[now]+e[i].val)
            {
                cost[e[i].to]=cost[now]+e[i].val;
                if (!vis[e[i].to])
                {
                    que.push(e[i].to);
                    vis[e[i].to]=true;
                }
                cnt1[e[i].to]++;
                if (cnt1[e[i].to]==n) return false;
            }
        }
    }
    return true;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&flag,&a,&b);
        if (flag!=3) scanf("%d",&c);
        if (flag==1) add(a,b,-c);
        if (flag==2) add(b,a,c);
        if (flag==3) 
        {
            add(a,b,0);
            add(b,a,0);
        }
    }
    for (int i=1;i<=n;i++)
        add(n+1,i,0);
    First(n+1);
    SPFA(n+1);
    if (SPFA(n+1)) printf("Yes");
    else printf("No");
    return 0;
}

你可能感兴趣的:(【Luogu 1993】差分约束系统问题——小K的农场)