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。它對每個節點的約束條件為:
最後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;
}