[洛谷 P1993]小K的农场 --- 差分约束 + spfa判环

本文介绍了一种解决洛谷P1993问题的方法,通过差分约束建图和DFS版SPFA算法判断农场作物数量是否符合模糊信息条件,避免负环出现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门:洛谷 P1993


题目描述

K K K M C MC MC里面建立很多很多的农场,总共 n n n个,以至于他自己都忘记了每个农场中种植作物的具体数量了,他只记得一些含糊的信息(共 m m m个),以下列三种形式描述:

  • 农场 a a a比农场 b b b至少多种植了 c c c个单位的作物,
  • 农场 a a a比农场 b b b至多多种植了 c c c个单位的作物,
  • 农场 a a a与农场 b b b种植的作物数一样多。

但是,由于小 K K K的记忆有些偏差,所以他想要知道存不存在一种情况,使得农场的种植作物数量与他记忆中的所有信息吻合。


分析

本题仅分为两部分,建图+判负环

  1. 建图 — 差分约束
    在一张的图上边 ( u , v , w ) (u, v, w) (u,v,w),必定满足不等式 d i s [ u ] + w ≥ d i s [ v ] dis[u] + w \geq dis[v] dis[u]+wdis[v],对于本题而言,也有类似的关系:
    f [ x ] f[x] f[x]表示农场 x x x的作物数量,则有
  • f [ a ] − f [ b ] ≥ c    ⟺    f [ a ] − c ≥ f [ b ] f[a] - f[b] \geq c \iff f[a] - c \geq f[b] f[a]f[b]cf[a]cf[b]
  • f [ a ] − f [ b ] ≤ c    ⟺    f [ b ] + c ≥ f [ a ] f[a] - f[b] \leq c \iff f[b] + c \geq f[a] f[a]f[b]cf[b]+cf[a]
  • f [ a ] = f [ b ]    ⟺    f [ a ] ≥ f [ b ] & f [ b ] ≥ f [ a ] f[a] = f[b] \iff f[a] \geq f[b] \quad \& \quad f[b] \geq f[a] f[a]=f[b]f[a]f[b]&f[b]f[a]
    那么类似的,将 a a a b b b看做节点, c c c − c -c c看做边权建图即可
  1. 判负环 — d f s dfs dfs s p f a spfa spfa
    由于题目只要求是否可行,那么就只要判负数就可以了。
    d f s dfs dfs版的判断依据:第二次访问该节点即存在负环
    优化 d i s dis dis数组可以不用赋初值,直接为 0 0 0即可。可以减少没必要的迭代。
    注意:图不一定联通,要 f o r for for一遍(不判实测 70 70 70

代码

#include <cstdio>
#include <cstdlib>
#include <cstring>

#define IL inline

using namespace std;

IL int read()
{
    char c = getchar();
    int sum = 0 ,k = 1;
    for(;'0' > c || c > '9'; c = getchar())
        if(c == '-') k = -1;
    for(;'0' <= c && c <= '9'; c = getchar()) sum = sum * 10 + c - '0';
    return sum * k;
}

int n, m;

int to[20005], nxt[20005], val[20005];
int cnt, last[10005];
IL void add(int u, int v, int w)
{
    //printf("%d %d %d\n", u, v, w);
    to[++cnt] = v; nxt[cnt] = last[u]; val[cnt] = w; last[u] = cnt;
}

bool vis[10005];
int dis[10005];

IL bool dfs_spfa(int u)
{
    vis[u] = 1;
    
    for(int i = last[u], v; (v = to[i]); i = nxt[i])
    if(dis[u] + val[i] < dis[v])
    {
        dis[v] = dis[u] + val[i];
        if(vis[v] || dfs_spfa(v)) return 1;
    }
    
    vis[u] = 0;
    return 0;
}

int main()
{
    n = read(); m = read();
    
    for(int k, x, y, z; m; --m)
    {
    	k = read(); x = read(); y = read();
    	if(k != 3) z = read();
    	if(k == 1) add(x, y, -z); else
    	if(k == 2) add(y, x, z); else
    	if(k == 3) { add(x, y, 0); add(y, x, 0); }
    }
    
    int flag;
    for(int i = 1; i <= n; ++i)
    if(!vis[i])
    {
    	flag = dfs_spfa(i);
    	if(flag) break;
    }
    
    printf("%s\n", flag ? "No" : "Yes" );
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值