ZOJ-2770 Burn the Linked Camp 差分约束
题意:告诉我们一系列的不等式,当然这些不等式都是两个变量之间的差值,而非和值。刘备拥有N个军营,每个军营都有一个人数的上限,现在陆逊的探子来报刘备的[a, b]军营总人数不低过某一个值,现在问根据这些答案陆逊是否能够正确推断出刘备至少在各营一共驻扎了多少部队,如果不能推出,输出“Bad Estimations”。
分析:根据给定的条件,我们设sum[i]表示刘备前i个军营一共驻扎了多少人,那么每个军营至多有多少人就能够通过不等式列出来。假设一共有三个军营,上限分别为A,B,C,那么有:
0 <= sum[1] - sum[0] <= A
0 <= sum[2] - sum[1] <= B
0 <= sum[3] - sum[2] <= C
而对于陆逊的探子信息,对[a, b]不少于K人转化为:
sum[b] - sum[a] >= K
将sum[x]看做是最短路里面的dis[x]那么根据满足的三角不等式,就能够推出图的边,我们也就能求解出一组合法的满足所有等式的结果。
接下来要求的刘备总共至少驻扎了多少部队,及设要求的量为M,则满足
sum[N] - sum[0] >= M 或 sum[0] - sum[N] <= -M
对于后面的不等式如果设sum[M] = 0,就变成了从N到0的最短路径求解。
代码如下:
#include <iostream> #include <cstring> #include <cstdio> #include <cstdlib> #include <queue> #include <algorithm> using namespace std; int N, M, idx, head[1005]; long long sum[1005]; char vis[1005]; int cnt[1005]; long long dis[1005]; struct Edge { int v, next; long long ct; }e[1050005]; void insert(int a, int b, long long ct) { e[idx].v = b, e[idx].ct = ct; e[idx].next = head[a]; head[a] = idx++; } void build() { for (int i = 1; i <= N; ++i) { insert(i-1, i, sum[i] - sum[i-1]); insert(i, i-1, 0); } } bool spfa() { memset(cnt, 0, sizeof (cnt)); memset(vis, 0, sizeof (vis)); memset(dis, 0x3f, sizeof (dis)); dis[N] = 0, vis[N] = 1; cnt[N] = 1; queue<int>q; q.push(N); while (!q.empty()) { int v = q.front(); q.pop(); vis[v] = 0; if (cnt[v] > N) return false; for (int i = head[v]; i != -1; i = e[i].next) { if (dis[e[i].v] > dis[v] + e[i].ct) { dis[e[i].v] = dis[v] + e[i].ct; if (!vis[e[i].v]) { q.push(e[i].v); ++cnt[e[i].v]; vis[e[i].v] = 1; } } } } return true; } int main() { int a, b, c; while (scanf("%d %d", &N, &M) == 2) { idx = 0; memset(head, 0xff, sizeof (head)); for (int i = 1; i <= N; ++i) { scanf("%I64d", &sum[i]); sum[i] += sum[i-1]; // 求一个前缀和 } for (int i = 0; i < M; ++i) { scanf("%d %d %d", &a, &b, &c); insert(b, a-1, -c); // 将a, b, c转化为图中的边 } build(); if (spfa()) { printf("%d\n", -dis[0]); } else { puts("Bad Estimations"); } } return 0; }