1. 程式人生 > >BZOJ 2330 [差分約束][最短路]

BZOJ 2330 [差分約束][最短路]

2330: [SCOI2011]糖果
Description
幼兒園裡有N個小朋友,lxhgww老師現在想要給這些小朋友們分配糖果,要求每個小朋友都要分到糖果。但是小朋友們也有嫉妒心,總是會提出一些要求,比如小明不希望小紅分到的糖果比他的多,於是在分配糖果的時候,lxhgww需要滿足小朋友們的K個要求。幼兒園的糖果總是有限的,lxhgww想知道他至少需要準備多少個糖果,才能使得每個小朋友都能夠分到糖果,並且滿足小朋友們所有的要求。
Input
輸入的第一行是兩個整數N,K。
接下來K行,表示這些點需要滿足的關係,每行3個數字,X,A,B。
如果X=1, 表示第A個小朋友分到的糖果必須和第B個小朋友分到的糖果一樣多;
如果X=2, 表示第A個小朋友分到的糖果必須少於第B個小朋友分到的糖果;
如果X=3, 表示第A個小朋友分到的糖果必須不少於第B個小朋友分到的糖果;
如果X=4, 表示第A個小朋友分到的糖果必須多於第B個小朋友分到的糖果;
如果X=5, 表示第A個小朋友分到的糖果必須不多於第B個小朋友分到的糖果;
Output
輸出一行,表示lxhgww老師至少需要準備的糖果數,如果不能滿足小朋友們的所有要求,就輸出-1。
Sample Input
5 7
1 1 2
2 3 2
4 4 1
3 4 5
5 4 5
2 3 5
4 5 1
Sample Output
11
HINT
【資料範圍】
對於30%的資料,保證 N<=100
對於100%的資料,保證 N<=100000
對於所有的資料,保證 K<=100000,1<=X<=5,1<=A, B<=N

本來是想著拓撲排序的
還是要學新東西
先講講差分約束系統吧。

如果有3個表示式

                                a - b <= k1
                                b - c <= k2
                                a - c <= k3

那麼我們現在要求a - c的最小值 顯然 a - c <= min(k1 + k2, k3)
那麼由此我們想到了最短路。

再看本題 總共五個條件,為了好處理,可以這樣:

                                a - b >= k1
                                b - c >= k2
                                a - c >= k3

改為求a - c的最小值 同理 a - c >= max(k1 + k2, k3)
那麼就相當於求最長路

本題中的條件可以轉化為

                                1:  a - b >= 0 && b - a >= 0
                                2:  b - a >= 1
                                3:  a - b >= 0
                                4:  a - b >= 1
                                5:  b - a >= 0

如此加邊, 起點為節點0。它對每個節點的約束條件為:ai - a0 >= 0

最後spfa

附程式碼

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <queue>
#define N 100010
using namespace std;

struct edge {
  int to, key, next;
  edge(void) {}
  edge(int t, int k, int n):to(t), key(k), next(n) {}
}G[N << 2];
int head[N], vis[N], dist[N], innum[N];
int n, m, x, a, b, cnt = 0;
long long ans = 0;
queue<int> Q;

inline void add_edge(int from, int to, int key) {
  G[cnt] = edge(to, key, head[from]);
  head[from] = cnt++;
}
inline char get(void) {
  static char buf[100000], *p1 = buf, *p2 = buf;
  if (p1 == p2) {
    p2 = (p1 = buf) + fread(buf, 1, 100000, stdin);
    if (p1 == p2) return EOF;
  }
  return *p1++;
}
inline void read(int &x) {
  x = 0; char c;
  for(c = get(); c < '0' || c > '9'; c = get());
  for(; c >= '0' && c <= '9'; x = (x << 1) + (x << 3) + c - '0', c = get());
}
bool spfa(int s) {
  memset(dist, 0, sizeof(dist));
  memset(vis, 0, sizeof(vis));
  memset(innum, 0, sizeof(innum));
  Q.push(s); vis[s] = 1; dist[s] = 0; innum[s]++;
  while(!Q.empty()) {
    int x = Q.front(); Q.pop();
    for(int i = head[x]; i != -1; i = G[i].next) {
      edge &e = G[i];
      if (dist[e.to] < dist[x] + e.key) {
        dist[e.to] = dist[x] + e.key;
        if (!vis[e.to]) {
          Q.push(e.to); vis[e.to] = 1;
          if (++innum[e.to] >= n) return 1;
        }
      }
    }
    vis[x] = 0;
  }
  return 0;
}

int main(void) {
  read(n); read(m);
  memset(head, -1, sizeof(head));
  for(int i = 0; i < m; i++) {
    read(x); read(a); read(b);
    switch (x) {
      case 1: {
        add_edge(a, b, 0); add_edge(b, a, 0);
        break;
      }
      case 2: {
        if (a == b) {puts("-1"); return 0;}
        add_edge(a, b, 1); break;
      }
      case 3: {
        add_edge(b, a, 0); break;
      }
      case 4: {
        if (a == b) {puts("-1"); return 0;}
        add_edge(b, a, 1); break;
      }
      case 5: {
        add_edge(a, b, 0); break;
      }
    }
  }
  for(int i = n; i > 0; i--) add_edge(0, i, 1);
  if (spfa(0)) {puts("-1"); return 0;}
  for(int i = 1; i <= n; i++) ans += dist[i];
  printf("%lld\n", ans);
  return 0;
}