推荐一片入门博客:夜深人静写算法(四) - 差分约束
个人感觉很有用的地方,暂存一下,备查:
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
*/