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;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值