在找图的相关题目时找到这道题,看了半天没有发现和图有哪门子关系,看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;
}