UVa 515 King(差分约束系统)

题目链接:UVa 515 King

差分约束系统。

如果一个系统由n个变量和m个约束条件组成,其中每个约束条件形如xj-xi<=bk(i,j∈[1,n],k∈[1,m]),则称其为差分约束系统(system of difference constraints)。亦即,差分约束系统是求解关于一组变量的特殊不等式组的方法。
求解差分约束系统,可以转化成图论的单源最短路径(或最长路径)问题。
观察xj-xi<=bk,会发现它类似最短路中的三角不等式d[v]<=d[u]+w[u,v],即d[v]-d[u]<=w[u,v]。因此,以每个变量xi为结点,对于约束条件xj-xi<=bk,连接一条边(i,j),边权为bk。我们再增加一个源点s,s与所有定点相连,边权均为0。对这个图,以s为源点运行Bellman-ford算法(或SPFA算法),最终{d[ i]}即为一组可行解。(来自百度百科)

本题利用前n个元素的和作为节点,即设s[i] = a[1] + a[2] + …a[i]。 

有a[si] + a[si+1] + … + a[si + ni] = s[si + ni] - s[si - 1],所以如果a[si] + a[si+1] + … + a[si + ni] < k 则 s[si + ni] - s[si - 1] < k <= k - 1;如果a[si] + a[si+1] + … + a[si + ni] > k 则 s[si - 1] - s[si + ni] < -k <= -k - 1;

需要注意的地方时这个题本身是存在0这个点的(我刚开始把0作为添加的源点果断错了),因为需要表示s[1]。

增加的源点作为第n + 1个点,那么总共是有n + 2个点的,所以用SPFA判断存在负环应该是一个点进入队列的次数大于等于n + 2。

#include <iostream>
#include <cstring>
#include <stdio.h>
#include <queue>

using namespace std;

const int MAX_N = 200 + 10;
int head[MAX_N],d[MAX_N],cnt[MAX_N],vis[MAX_N];
const int INF = (1 << 30);
struct Edge
{
    int v, w, next;
};
Edge edge[MAX_N];
int n, m, _count;
void addEdge(int u, int v, int w)
{
    edge[_count].v = v;
    edge[_count].w = w;
    edge[_count].next = head[u];
    head[u] = _count++;
}
bool SPFA()
{
    memset(vis,0,sizeof(vis));
    memset(cnt,0,sizeof(cnt));
    for(int i = 0;i <= n;i++)
        d[i] = INF;
    d[n + 1] = 0;
    queue<int> Q;
    Q.push(n + 1);
    cnt[n + 1]++;
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        vis[u] = false;
        for(int e = head[u]; e != -1; e = edge[e].next)
        {
            if(d[edge[e].v] > d[u] + edge[e].w)
            {
                d[edge[e].v] = d[u] + edge[e].w;
                if(!vis[edge[e].v])
                {
                    Q.push(edge[e].v);
                    vis[edge[e].v] = true;
                    cnt[edge[e].v]++;
                    if(cnt[edge[e].v] >= n + 2)// 0 ~ n + 1 共有n + 2个点
                        return false;
                }
            }
        }
    }
    return true;
}
int main()
{
    while(scanf("%d", &n), n)
    {
        scanf("%d", &m);
        int s, len, k;
        char str[10];
        _count = 0;
        memset(head, -1, sizeof(head));
        for(int i = 0; i <= n; i++)
            addEdge(n + 1, i, 0);//添加从n + 1 到所有点的路径,设置边权为0
        for(int i = 1; i <= m; i++)
        {
            scanf("%d%d%s%d", &s, &len, str, &k);
            if(str[0] == 'g')//将大于转换为小于等于
                addEdge(s + len, s - 1, -k - 1);
            else//将小于转化为小于等于
                addEdge(s - 1, s + len, k - 1);
        }
        if(!SPFA())
            printf("successful conspiracy\n");
        else
            printf("lamentable kingdom\n");
    }
    return 0;
}


你可能感兴趣的:(UVa 515 King(差分约束系统))