sgu 298 差分约束(特定解)

题目:http://acm.sgu.ru/problem.php?contest=0&problem=298
题意:

给定n个点m条约束。
下面输出 u v x 表示:
dis[u] - dis[v] >= x
然后建图就是 u->v 边权为-x
输出一个解满足 -10000<= dis[i] <= 10000。
若有多解输出一个 dis[n] - dis[1] 最小的解。

分析:

最短路得到最大解,最长路得到最小解,然后判断是否最大解全部不小于最小解。
因为要求dis[n]-dis[1]最小,那就令dis[n]为其最小解,dis[1]为其最大解,再spfa一遍就好。

有一个连等式:(规定求最短路的那个图为正向边和正权)正向边正权最短路的解的结构=反向边反权最长路解的结构=正向边反权最长路解的值取相反数之后的结构=反向边正权最短路解的值取相反数之后的结构。

代码:


const int N = 100000 + 9;
struct Edge {
    int v, w, next;
};
Edge edge[N], fedge[N];
int head[N], fhead[N], d[N], fd[N], vis[N], num[N], n, cnt;
void addedge (int u, int  v, int w) {
    edge[cnt].v = v;
    edge[cnt].w = w;
    edge[cnt].next = head[u];
    head[u] = cnt;
    fedge[cnt].v = u;
    fedge[cnt].w = w;
    fedge[cnt].next = fhead[v];
    fhead[v] = cnt++;

}

bool spfa (int head[], Edge edge[], int d[]) {
    queue<int>q;
    for (int i = 1; i <= n; i++) num[i] = 0, vis[i] = 1, q.push (i);
    while (!q.empty() ) {
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for (int i = head[u]; ~i; i = edge[i].next) {
            if (d[edge[i].v] > d[u] + edge[i].w) {
                d[edge[i].v] = d[u] + edge[i].w;
                if (!vis[edge[i].v]) {
                    q.push (edge[i].v);
                    vis[edge[i].v] = 1;
                    if (++num[edge[i].v] > n) return 0;
                }
            }
        }
    }
    return 1;
}
int solve() {
    for (int i = 1; i <= n; i++) d[i] = fd[i] = INF;
    if (!spfa (head, edge, d) ) return 0;
    if (!spfa (fhead, fedge, fd) ) return 0;
    for (int i = 1; i <= n; i++)
        if (d[i] < -fd[i]) return 0;
    d[n] = -fd[n];
    spfa (head, edge, d);
    for (int i = 1; i < n; i++) printf ("%d ", d[i]);
    printf ("%d\n", d[n]);
    return 1;
}
int main() {
    // freopen ("f.txt", "r", stdin);
    int u, v, w, m;
    while (~scanf ("%d%d", &n, &m) ) {
        memset (head, -1, sizeof (head) );
        memset (fhead, -1, sizeof (fhead) );
        cnt = 0;

        for (int i = 0; i < m; i++) {
            scanf ("%d%d%d", &u, &v, &w);
            addedge (u, v, -w);
        }

        if (!solve() ) puts ("-1");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值