POJ ~ 3159 ~ Candies (Dijkstra + 优先队列 + 链式前向星 or 栈式SPFA)(差分约束)

推荐一片入门博客夜深人静写算法(四) - 差分约束

个人感觉很有用的地方,暂存一下,备查:

  4、最大值 => 最小值
      然后,我们将问题进行一个简单的转化,将原先的"<="变成">=",转化后的不等式如下:
B - A >= c      (1)
C - B >= a      (2)
C - A >= b      (3)
      然后求C - A的最小值,类比之前的方法,需要求的其实是max{b, c+a},于是对应的是图三-2-1从A到C的最长路。同样可以推广到n个变量m个不等式的情况。
      
      5、不等式标准化
      如果给出的不等式有"<="也有">=",又该如何解决呢?很明显,首先需要关注最后的问题是什么,如果需要求的是两个变量差的最大值,那么需要将所有不等式转变成"<="的形式,建图后求最短路;相反,如果需要求的是两个变量差的最小值,那么需要将所有不等式转化成">=",建图后求最长路。
      如果有形如:A - B = c 这样的 等式呢?我们可以将它转化成以下两个不等式:
A - B >= c      (1)
A - B <= c      (2)
       再通过上面的方法将其中一种不等号反向,建图即可。
       最后,如果这些变量都是整数域上的,那么遇到A - B < c这样的不带等号的不等式,我们需要将它转化成"<="或者">="的形式,即 A - B <= c - 1。

以上内容出自,上面链接中的博客。

题意:有N个小朋友要分糖果,现在告诉你M组关系,A比B分的少分糖果不能多于C(即B-A>=C),问n比1最多多少个糖果?

思路:感觉差分约束这门都不让入了,害怕,第一道题就这么坑。题目中的B - A >= C 即 B >=  A  + C,可以看作A~B有一条权值为C的边,要使dis[n]最大,那么也就是在这个图中,求1~N最短路。

坑点:Dijkstra + 优先队列 + 链式前向星(1375险过) or 栈式SPFA(688ms)。其他的方法都超时了。

Dijkstra + 优先队列 + 链式前向星:

TLE的交一下C++,我是1375险过。

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int MAXN = 30005;
const int MAXM = 150005;
const int INF = 0x3f3f3f3f;
struct Edge
{
    int to, dist, next;
}edge[MAXM];
int n, m, head[MAXN], d[MAXN];
bool vis[MAXN];
struct HeadNode
{
    int from, dist;
    bool operator < (const HeadNode& rhs) const
    {
        return rhs.dist < dist;
    }
    HeadNode(int u, int w): from(u), dist(w) {}
};
void add_edge(int u, int v, int w, int i)
{
    edge[i].to = v;
    edge[i].dist = w;
    edge[i].next = head[u];
    head[u] = i;
}
void Dijkstra(int s)
{
    for (int i = 0; i <= n; i++) d[i] = INF;
    memset(vis, false, sizeof(vis));
    d[s] = 0;
    priority_queue<HeadNode> Q;
    Q.push(HeadNode(s, 0));
    while (!Q.empty())
    {
        HeadNode x = Q.top(); Q.pop();
        int u = x.from;
        vis[u] = false;
        if (vis[u]) continue;
        vis[u] = true;
        for (int i = head[u]; i != -1; i = edge[i].next)
        {
            Edge& e = edge[i];
            if (!vis[e.to] && d[e.to] > d[u] + e.dist)//!vis[e.to]不能省略,否则超时
            {
                d[e.to] = d[u] + e.dist;
                Q.push(HeadNode(e.to, d[e.to]));
            }
        }
    }
}
int main()
{
    while (~scanf("%d%d", &n, &m))
    {
        memset(head, -1, sizeof(head));
        for (int i = 0; i < m; i++)
        {
            int a, b, c;
            scanf("%d%d%d", &a, &b, &c);
            add_edge(a, b, c, i);//B-A<=c
        }
        Dijkstra(1);
        printf("%d\n", d[n]);
    }
    return 0;
}
/*
2 2
1 2 5
2 1 4
*/



栈式SPFA:

668ms,这快的害怕。队列就超时,栈就AC,为什么,很奇怪。

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<stack>
#include<cstring>
using namespace std;
const int MAXN = 30005;
const int MAXM = 150005;
const int INF = 0x3f3f3f3f;
struct Edge
{
    int to, dist, next;
}edge[MAXM];
int n, m, head[MAXN], d[MAXN];
bool vis[MAXN];
void add_edge(int u, int v, int w, int i)
{
    edge[i].to = v;
    edge[i].dist = w;
    edge[i].next = head[u];
    head[u] = i;
}
void SPFA(int s)
{
    for (int i = 0; i <= n; i++) d[i] = INF;
    memset(vis, false, sizeof(vis));
    d[s] = 0; vis[s] = true;
    stack<int> S;
    S.push(s);
    while (!S.empty())
    {
        int u = S.top(); S.pop();
        vis[u] = false;
        for (int i = head[u]; i != -1; i = edge[i].next)
        {
            Edge& e = edge[i];
            if (d[u] < INF && d[e.to] > d[u] + e.dist)
            {
                d[e.to] = d[u] + e.dist;
                if (!vis[e.to])
                {
                    S.push(e.to); vis[e.to] = true;
                }
            }
        }
    }
}
int main()
{
    while (~scanf("%d%d", &n, &m))
    {
        memset(head, -1, sizeof(head));
        for (int i = 0; i < m; i++)
        {
            int a, b, c;
            scanf("%d%d%d", &a, &b, &c);
            add_edge(a, b, c, i);//B-A<=c
        }
        SPFA(1);
        printf("%d\n", d[n]);
    }
    return 0;
}
/*
2 2
1 2 5
2 1 4
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值