不懂的差分约束原理的点下面链接:
spfa算法
例题1 P5960【模板】差分约束算法
题目描述
给出一组包含 m m m 个不等式,有 n n n 个未知数的形如:
{ x c 1 − x c 1 ′ ≤ y 1 x c 2 − x c 2 ′ ≤ y 2 ⋯ x c m − x c m ′ ≤ y m \begin{cases} x_{c_1}-x_{c'_1}\leq y_1 \\x_{c_2}-x_{c'_2} \leq y_2 \\ \cdots\\ x_{c_m} - x_{c'_m}\leq y_m\end{cases} ⎩ ⎨ ⎧xc1−xc1′≤y1xc2−xc2′≤y2⋯xcm−xcm′≤ym
的不等式组,求任意一组满足这个不等式组的解。
输入格式
第一行为两个正整数 n , m n,m n,m,代表未知数的数量和不等式的数量。
接下来 m m m 行,每行包含三个整数 c , c ′ , y c,c',y c,c′,y,代表一个不等式 x c − x c ′ ≤ y x_c-x_{c'}\leq y xc−xc′≤y。
输出格式
一行,
n
n
n 个数,表示
x
1
,
x
2
⋯
x
n
x_1 , x_2 \cdots x_n
x1,x2⋯xn 的一组可行解,如果有多组解,请输出任意一组,无解请输出 NO
。
样例 #1
样例输入 #1
3 3
1 2 3
2 3 -2
1 3 1
样例输出 #1
5 3 5
提示
样例解释
{ x 1 − x 2 ≤ 3 x 2 − x 3 ≤ − 2 x 1 − x 3 ≤ 1 \begin{cases}x_1-x_2\leq 3 \\ x_2 - x_3 \leq -2 \\ x_1 - x_3 \leq 1 \end{cases} ⎩ ⎨ ⎧x1−x2≤3x2−x3≤−2x1−x3≤1
一种可行的方法是 x 1 = 5 , x 2 = 3 , x 3 = 5 x_1 = 5, x_2 = 3, x_3 = 5 x1=5,x2=3,x3=5。
{ 5 − 3 = 2 ≤ 3 3 − 5 = − 2 ≤ − 2 5 − 5 = 0 ≤ 1 \begin{cases}5-3 = 2\leq 3 \\ 3 - 5 = -2 \leq -2 \\ 5 - 5 = 0\leq 1 \end{cases} ⎩ ⎨ ⎧5−3=2≤33−5=−2≤−25−5=0≤1
数据范围
对于 100 % 100\% 100% 的数据, 1 ≤ n , m ≤ 5 × 1 0 3 1\leq n,m \leq 5\times 10^3 1≤n,m≤5×103, − 1 0 4 ≤ y ≤ 1 0 4 -10^4\leq y\leq 10^4 −104≤y≤104, 1 ≤ c , c ′ ≤ n 1\leq c,c'\leq n 1≤c,c′≤n, c ≠ c ′ c \neq c' c=c′。
评分策略
你的答案符合该不等式组即可得分,请确保你的答案中的数据在 int
范围内。
如果并没有答案,而你的程序给出了答案,SPJ 会给出 There is no answer, but you gave it
,结果为 WA;
如果并没有答案,而你的程序输出了 NO
,SPJ 会给出 No answer
,结果为 AC;
如果存在答案,而你的答案错误,SPJ 会给出 Wrong answer
,结果为 WA;
如果存在答案,且你的答案正确,SPJ 会给出 The answer is correct
,结果为 AC。
思路
可以看此题洛谷的第一篇题解,很详细的解释了差分约束,我就不多讲了。点此处
此题将其题目移向,变成
{
x
1
≤
x
2
+
3
x
2
≤
x
3
−
2
x
1
≤
x
3
+
1
\begin{cases}x_1\leq x_2+3 \\ x_2 \leq x_3 -2\\ x_1 \leq x_3 +1\end{cases}
⎩
⎨
⎧x1≤x2+3x2≤x3−2x1≤x3+1
这里就形如求最短路时的三角不等式 dist[i]<=dist[j]+k,转化成了一个图论问题啦。
这里只要求一个解就可以,所以求最长路和最短路都可以,我用的是最短路。
#include<bits/stdc++.h>
using namespace std;
int n,m;
const int N=5e3+5;
int dist[N],vis[N],cnt[N];
struct node{
int to;
int len;
};
vector<node>g[N];
bool spfa()
{
memset(dist,0x3f,sizeof dist); //初始化最大,因为求最短
dist[0]=0;
vis[0]=1;
queue<int>q;
q.push(0);
while(q.size())
{
int x=q.front();
q.pop();
vis[x]=0;
for(auto i:g[x])
{
if(dist[i.to]>dist[x]+i.len)
{
dist[i.to]=dist[x]+i.len;
cnt[i.to]=cnt[x]+1;
if(cnt[i.to]>=n+1) return false; //判断有无负环
if(!vis[i.to])
vis[i.to]=1,q.push(i.to);
}
}
}
return true;
}
int main()
{
cin>>n>>m;
while(m--)
{
int u,v,w;
cin>>u>>v>>w;
g[v].push_back({u,w});
}
for(int i=1;i<=n;i++)
g[0].push_back({i,0});
if(spfa())
{
for(int i=1;i<=n;i++)
cout<<dist[i]<<" ";
}
else cout<<"NO";
}
例题2 P1250 种树
种树
题目背景
一条街的一边有几座房子,因为环保原因居民想要在路边种些树。
题目描述
路边的地区被分割成块,并被编号成 1 , 2 , … , n 1, 2, \ldots,n 1,2,…,n。每个部分为一个单位尺寸大小并最多可种一棵树。
每个居民都想在门前种些树,并指定了三个号码 b b b, e e e, t t t。这三个数表示该居民想在地区 b b b 和 e e e 之间(包括 b b b 和 e e e)种至少 t t t 棵树。
居民们想种树的各自区域可以交叉。你的任务是求出能满足所有要求的最少的树的数量。
输入格式
输入的第一行是一个整数,代表区域的个数 n n n。
输入的第二行是一个整数,代表房子个数 h h h。
第 3 3 3 到第 ( h + 2 ) (h + 2) (h+2) 行,每行三个整数,第 ( i + 2 ) (i + 2) (i+2) 行的整数依次为 b i , e i , t i b_i, e_i, t_i bi,ei,ti,代表第 i i i 个居民想在 b i b_i bi 和 e i e_i ei 之间种至少 t i t_i ti 棵树。
输出格式
输出一行一个整数,代表最少的树木个数。
样例 #1
样例输入 #1
9
4
1 4 2
4 6 2
8 9 2
3 5 2
样例输出 #1
5
提示
数据规模与约定
对于 100 % 100\% 100% 的数据,保证:
- 1 ≤ n ≤ 3 × 1 0 4 1 \leq n \leq 3 \times 10^4 1≤n≤3×104, 1 ≤ h ≤ 5 × 1 0 3 1 \leq h \leq 5 \times 10^3 1≤h≤5×103。
- 1 ≤ b i ≤ e i ≤ n 1 \leq b_i \leq e_i \leq n 1≤bi≤ei≤n, 1 ≤ t i ≤ e i − b i + 1 1 \leq t_i \leq e_i - b_i + 1 1≤ti≤ei−bi+1。
思路
1.该题变换的有点多,首先求的是最少的树木个数,这里用最长路比较好
2.然后给的数据是区间里面至少中几棵树,所以我们应该想到前缀和
有以下不等式:
s
u
m
r
−
s
u
m
l
−
1
>
=
T
sum_r-sum_{l-1}>=T
sumr−suml−1>=T
转换为
s
u
m
r
>
=
s
u
m
l
−
1
+
T
sum_r >=sum_{l-1}+T
sumr>=suml−1+T
每个部分为一个单位尺寸大小并最多可种一棵树:
0
<
=
s
u
m
i
−
s
u
m
i
−
1
<
=
1
0<=sum_i-sum_{i-1}<=1
0<=sumi−sumi−1<=1
转换为
s
u
m
i
>
=
s
u
m
i
−
1
+
0
sum_i>=sum_{i-1}+0
sumi>=sumi−1+0 ,
s
u
m
i
−
1
>
=
s
u
m
i
−
1
sum_{i-1}>=sum_i-1
sumi−1>=sumi−1
3.然后需要注意的是
s
u
m
i
>
=
s
u
m
i
−
1
+
0
sum_i>=sum_{i-1}+0
sumi>=sumi−1+0 ,
s
u
m
i
−
1
>
=
s
u
m
i
−
1
sum_{i-1}>=sum_i-1
sumi−1>=sumi−1存图的时候,把0也存进去了(这是没有影响的,我们知道前缀和sum[0]=0),所以我们设的源点为n+1,所以图里面现在有n+2个点,测试数据没有存在环的情况,如果想添加判断环的情况也可以,这里我添加了一下。
下面看代码吧
#include<bits/stdc++.h>
using namespace std;
const int N=3e4+5;
int dist[N],vis[N],cnt[N];
struct node{
int to;
int len;
};
int n,h;
vector<node>g[N];
bool spfa()
{
memset(dist,-0x3f,sizeof dist);
dist[n+1]=0;
vis[n+1]=1;
queue<int>q;
q.push(n+1);
while(q.size())
{
int x=q.front();
q.pop();
vis[x]=0;
for(auto i:g[x])
{
if(dist[i.to]<dist[x]+i.len)
{
dist[i.to]=dist[x]+i.len;
dist[i.to]=dist[x]+i.len;
cnt[i.to]=cnt[x]+1;
if(cnt[i.to]>n+1) //因为现在有n+2个顶点了
{
cout<<-1;
return false; //负环情况
}
if(!vis[i.to]) q.push(i.to),vis[i.to]=1;
}
}
}
return true;
}
int main()
{
cin>>n>>h;
while(h--)
{
int l,r,t;
cin>>l>>r>>t;
g[l-1].push_back({r,t});
}
for(int i=1;i<=n;i++)
{
g[i].push_back({i-1,-1});
g[i-1].push_back({i,0});
}
for(int i=0;i<=n;i++) //加入源点n+1;
g[n+1].push_back({i,0});
if(spfa())
cout<<dist[n];
}
例题3
这个我也是看别人的,链接放这里了
THE MATRIX PROBLEM(差分约束)_BadGuy523的博客-CSDN博客
好啦,例题暂时更新到这了,后面有的话会再更新的。