差分约束算法例题

不懂的差分约束原理的点下面链接:
spfa算法

算法差分约束

图论学习差分约束

例题1 P5960【模板】差分约束算法

洛谷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} xc1xc1y1xc2xc2y2xcmxcmym

的不等式组,求任意一组满足这个不等式组的解。

输入格式

第一行为两个正整数 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 xcxcy

输出格式

一行, n n n 个数,表示 x 1 , x 2 ⋯ x n x_1 , x_2 \cdots x_n x1,x2xn 的一组可行解,如果有多组解,请输出任意一组,无解请输出 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} x1x23x2x32x1x31

一种可行的方法是 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} 53=2335=2255=01

数据范围

对于 100 % 100\% 100% 的数据, 1 ≤ n , m ≤ 5 × 1 0 3 1\leq n,m \leq 5\times 10^3 1n,m5×103 − 1 0 4 ≤ y ≤ 1 0 4 -10^4\leq y\leq 10^4 104y104 1 ≤ c , c ′ ≤ n 1\leq c,c'\leq n 1c,cn 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} x1x2+3x2x32x1x3+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 1n3×104 1 ≤ h ≤ 5 × 1 0 3 1 \leq h \leq 5 \times 10^3 1h5×103
  • 1 ≤ b i ≤ e i ≤ n 1 \leq b_i \leq e_i \leq n 1biein 1 ≤ t i ≤ e i − b i + 1 1 \leq t_i \leq e_i - b_i + 1 1tieibi+1

思路

1.该题变换的有点多,首先求的是最少的树木个数,这里用最长路比较好
2.然后给的数据是区间里面至少中几棵树,所以我们应该想到前缀和
有以下不等式:

s u m r − s u m l − 1 > = T sum_r-sum_{l-1}>=T sumrsuml1>=T
转换为
s u m r > = s u m l − 1 + T sum_r >=sum_{l-1}+T sumr>=suml1+T

每个部分为一个单位尺寸大小并最多可种一棵树:
0 < = s u m i − s u m i − 1 < = 1 0<=sum_i-sum_{i-1}<=1 0<=sumisumi1<=1
转换为
s u m i > = s u m i − 1 + 0 sum_i>=sum_{i-1}+0 sumi>=sumi1+0 , s u m i − 1 > = s u m i − 1 sum_{i-1}>=sum_i-1 sumi1>=sumi1

3.然后需要注意的是
s u m i > = s u m i − 1 + 0 sum_i>=sum_{i-1}+0 sumi>=sumi1+0 , s u m i − 1 > = s u m i − 1 sum_{i-1}>=sum_i-1 sumi1>=sumi1存图的时候,把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博客

好啦,例题暂时更新到这了,后面有的话会再更新的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值