1. 程式人生 > >[洛谷 P1993]小K的農場 --- 差分約束 + spfa判環

[洛谷 P1993]小K的農場 --- 差分約束 + spfa判環

傳送門:洛谷 P1993


題目描述

K K M C MC 裡面建立很多很多的農場,總共 n

n 個,以至於他自己都忘記了每個農場中種植作物的具體數量了,他只記得一些含糊的資訊(共 m m 個),以下列三種形式描述:

  • 農場 a a
    比農場 b b 至少多種植了 c c 個單位的作物,
  • 農場 a
    a
    比農場 b b 至多多種植了 c c 個單位的作物,
  • 農場 a a 與農場 b b 種植的作物數一樣多。

但是,由於小 K K 的記憶有些偏差,所以他想要知道存不存在一種情況,使得農場的種植作物數量與他記憶中的所有資訊吻合。


分析

本題僅分為兩部分,建圖+判負環

  1. 建圖 — 差分約束
    在一張的圖上邊 ( u , v , w ) (u, v, w) ,必定滿足不等式 d i s [ u ] + w d i s [ v ] dis[u] + w \geq dis[v] ,對於本題而言,也有類似的關係:
    f [ x ] f[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 ] c       f [ b ] + c f [ a ] f[a] - f[b] \leq c \iff f[b] + c \geq f[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]
    那麼類似的,將 a a b b 看做節點, c c c -c 看做邊權建圖即可
  1. 判負環 — d f s dfs s p f a spfa
    由於題目只要求是否可行,那麼就只要判負數就可以了。
    d f s dfs 版的判斷依據:第二次訪問該節點即存在負環
    優化 d i s dis 陣列可以不用賦初值,直接為 0 0 即可。可以減少沒必要的迭代。
    注意:圖不一定聯通,要 f o r for 一遍(不判實測 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;
}