差分约束系统

差分约束系统定义(引自维基百科):如果一个系统由n个变量和m个约束条件组成,其中每个约束条件形如xj-xi<=bk(i,j∈[1,n],k∈[1,m]),则称其为差分约束系统(system of difference constraints)。亦即,差分约束系统是求解关于一组变量的特殊不等式组的方法。

例如,有如下不等式组:


我们可以把它写成如下矩阵的形式:

差分约束系统_第1张图片

定义左边的矩阵为A,中间的列向量为 x, 右边的列向量为k。

我们观察一下A可以发现它和我们在离散数学中讲图的时候讲了一个关联矩阵很像。

矩阵A的特点是每行只有一个1和一个-1,其余列都是0。

而关联矩阵的特点是每列只有一个1和一个-1,其余行为0.

即A的转置矩阵就是一个关联矩阵。

那么我们想想,是否可以通过求解图的最短路径来求得不等式组的一个可行解呢?

结论是可以的。

对于每一个差分约束系统,我们根据矩阵A来建立图G(V, E),而G的关联矩阵就是A的转置矩阵,边权存在二维数组W中。假设我们现在要求单源最短路,将各个定点的最短距离存到对应的dis[v]中。最后我们要得到的结果一定满足: dis[v] <= dis[u] + w[u][v]. 变形一下得到: dis[v] - dis[u] <= w[u][v].   另dis[v] 为 Xv, dis[u]为Xu,有Xv - Xu <= w[u][v]. 可以看到当前单源最短路问题与差分约束系统问题的等价性。

故可以通过求解单源最短路来求解差分约束问题。当图中存在负环的时候,不等式组无解,否则有解,并且xi的解为dis[i]。若dis[i] = INF,则xi的值为任意值。


hdu1531是一道入门题。

题意:给定未知数的个数n,同时给出m个约束条件, 均以(Asi + Asi+1  + ... + Asi+n) < k 或者 (Asi + Asi+1  + ... + Asi+n) > k形式出现。问是否存在可行解。

定义一下sigma(n) = A1 + A2 + A3 + ... + An.

则 Ai + ... + Aj = sigma(j) - sigma(i-1).这样就可以求解了。


代码:

#include
#include
#include

using namespace std;

const int N = 128;
const int INF = 0x3f3f3f3f;

int n, m, cnt;

struct Edge {
    int v, w;
    int next;
};

int fir[N];
Edge edge[N];
int vis[N], dis[N];
int outque[N];

void init() {
    cnt = 1;
    memset(fir, -1, sizeof(fir));
}

void addEdge(int u, int v, int w) {
    edge[cnt].v = v;
    edge[cnt].w = w;
    edge[cnt].next = fir[u];
    fir[u] = cnt++;
}

bool SPFA(int s) {
    queue que;
    memset(vis, 0, sizeof(vis));
    memset(dis, INF, sizeof(dis));
    memset(outque, 0, sizeof(outque));
    dis[s] = 0;
    que.push(s);
    while(!que.empty()) {
        int cur = que.front();
        que.pop();
        outque[cur]++;
        if(outque[cur] > n) return false;
        vis[cur] = 0;
        for(int i = fir[cur]; i != -1; i = edge[i].next) {
            int v = edge[i].v;
            if(dis[v] > dis[cur] + edge[i].w) {
                dis[v] = dis[cur] + edge[i].w;
                if(!vis[v]) {
                    vis[v] = 1;
                    que.push(v);
                }
            }
        }
    }
    return true;
}

int main() {

    while(~scanf("%d", &n), n) {
        init();
        scanf("%d", &m);
        for(int i = 0; i < m; i++) {
            int s, ni, k;
            char o[6];
            scanf("%d%d%s%d", &s, &ni, o, &k);
            if(o[0] == 'g')
                addEdge(s + ni, s - 1, -k-1);
            else addEdge(s - 1, s + ni, k - 1);
        }

        for(int i = 1; i <= n; i++) {
            addEdge(n+1, i, 0);
        }

        if(SPFA(n+1))
            printf("lamentable kingdom\n");
        else printf("successful conspiracy\n");
    }
    return 0;
}



你可能感兴趣的:(差分约束,算法,图论,c++)