在找图的相关题目时找到这道题,看了半天没有发现和图有哪门子关系,看discuss里面才了解到这个叫做“差分约束系统”的东东。赶紧百度一下,学习学习。纠结了我几天,受益匪浅。花了我30多个 submit来测试。才搞懂了。。。
题目大意:已知一个序列 s[1...n],给出它的 m个子序列以及对该子序列的约束条件,例如 s[a],s[a+1],s[a+2],...,s[a+b];且s[a]+s[a+1]+s[a+2]+...+s[a+b] < or > k。问题关键在于如何转化约束条件,用前n项和来转化成两两之间的关系。如:s[a]+s[a+1]+...+s[a+b] < k 可以转化成前n项和 sum[a+b]-sum[a-1] < k,为了能用Bellman_Ford,即将'<'转化成'<=',可以写成sum[a+b]-sum[a-1] <= k-1.若是 s[a]+s[a+1]+...+s[a+b] > k 可转化成 sum[a+b]-sum[a-1] >= k+1, 即 sum[a-1]-sum[a+b] <= -k-1;
Bellman-Ford算法实现:
#include <stdio.h> #define INF 100000000 struct { int s, e, v; } edge[105]; int n, nedge; int dis[105]; void add(int s, int e, int v) { edge[nedge].s = s; edge[nedge].e = e; edge[nedge].v = v; nedge++; } int relax(int s, int e, int v) { if (dis[s]+v < dis[e]) { dis[e] = dis[s]+v; return 1; } return 0; } int BellmanFord(int s0) { int i, j; //Initialize for (i=0; i<=n; i++) dis[i] = INF; dis[s0] = 0; //Relax for (i=1; i<=n; i++) //引入一个源点0之后就有n+1个顶点了 for (j=0; j<nedge; j++) relax(edge[j].s, edge[j].e, edge[j].v); //Check Negative power circuit for (i=0; i<nedge; i++) if (relax(edge[i].s, edge[i].e, edge[i].v)) return 1; return 0; } int main() { int m, a, b, k; char c[4]; while (scanf("%d", &n) && n) { scanf("%d", &m); nedge = 0; while (m--) { scanf("%d %d %s %d", &a, &b, c, &k); if (c[0] == 'l') add(a-1, a+b, k-1); else add(a+b, a-1, -k-1); } if (BellmanFord(0)) printf("successful conspiracy/n"); else printf("lamentable kingdom/n"); } return 0; }
SPFA算法实现:
#include <stdio.h> #include <string.h> #include <stdlib.h> #define INF 100000000 struct EDGE { int nd; //终点 int eg; //边权 struct EDGE *nxt; }; struct { struct EDGE *head, *last; } node[120]; /* 邻接表存储 */ int n; int dis[120]; int Q[20000], vst[120], cnt[120]; void add(int s, int e, int v) { struct EDGE *p; p = (struct EDGE*)malloc(sizeof(struct EDGE)); p->nd = e; p->eg = v; p->nxt = NULL; if (node[s].head == NULL) { node[s].head = p; node[s].last = p; } else { node[s].last->nxt = p; node[s].last = p; } } int relax(int s, int e, int v) { if (dis[s]+v < dis[e]) { dis[e] = dis[s]+v; return 1; } return 0; } /* 队列实现SPFA */ int SPFA(int s0) { int i, p, q; struct EDGE *pp; memset(vst, 0, sizeof(vst)); memset(cnt, 0, sizeof(cnt)); for (i=0; i<=n+1; i++) dis[i] = INF; dis[s0] = 0; Q[0] = s0; p = 0; q = 1; vst[s0] = 1; cnt[s0]++; while (p < q) { pp = node[Q[p]].head; while (pp) { if (relax(Q[p], pp->nd, pp->eg) && !vst[pp->nd]) { Q[q++] = pp->nd; vst[pp->nd] = 1; cnt[pp->nd]++; if (cnt[pp->nd] > n+1) return 1; } pp = pp->nxt; } vst[Q[p]] = 0; p++; } return 0; } int main() { int m, a, b, k; char c[4]; while (scanf("%d", &n) && n) { memset(node, 0, sizeof(node)); scanf("%d", &m); while (m--) { scanf("%d %d %s %d", &a, &b, c, &k); if (c[0] == 'l') add(a-1, a+b, k-1); else add(a+b, a-1, -k-1); } //附加n+1点当做源点 for (k=0; k<=n; k++) add(n+1, k, 0); if (SPFA(n+1)) printf("successful conspiracy/n"); else printf("lamentable kingdom/n"); } return 0; }