差分约束的大概样子 (大概)

差分约束一般是由特殊的n元一次不等式组组成的,它包含N个变量X1-Xn和M个约束条件,而且每个约束条件都是由两个变量做差组成的,形如,其中的ck是常数,它需要我们找到一组解使得每个不等式都成立。

关键算法:图的基本算法,不等式,SPFA求负环和最短(长)路。

具体请看下面的例题:

目录

题目 AcWing-362 区间 (模板)

题目 AcWing-2128 狡猾的商人 (等式转化为不等式)

题目 AcWing-3265 再卖菜

题目 AcWing-393 雇佣收银员


题目 AcWing-362 区间 (模板)

AcWing-362 区间

给定  个区间  和  个整数 

你需要构造一个整数集合 ,使得  中满足  的整数  不少于  个。

求这样的整数集合  最少包含多少个数。

思路

首先,这应该是差分约束的模板题,AcWing的题解区已经提供了非常多的高质量优秀解法(不限于差分约束解法),所以我就说明一下解题步骤好了:(s数组表示前缀和)

1.首先我们先想个办法把原始题目搞成差分的形式:

让区间内的整数个数不少于个 ——>  

这样我们看到了差分约束的模型。

2.差分约束比较考验我们寻找不等式的能力,我们还需要从题目中挖掘出以下两个不等式:

,区间中被选择的数字只能增加不能减少;

,对于每一个数,我们只能选择它一次。

3.现在我们需要把不等式整理为固定的形式:目标主要是第三个不等式,因为它不等号方向与其他两个不一致:(目标不等式范例 )

4.建立一个有向图,并在图中连接以下与上面提到的不等式相对应的边:

 权值为 ;(根据题目提供的数据建立边)

 权值为 0;(从 s[0] 到 s[50000] )

 权值为 -1;(从 s[0] 到 s[50000] )

5.使用SPFA求解图中单源最长路,求到s[50000]的值,这个值就是答案。

代码

#include <bits/stdc++.h>
using namespace std;#define Rint register int //just for fun
#define NINF 0xCF
#define MAXN 100005int n;
int head[MAXN], ver[2*MAXN], edge[2*MAXN], Next[2*MAXN], tot;
void g_add (int x, int y, int z) {ver[++tot]=y; edge[tot]=z; Next[tot]=head[x]; head[x]=tot;
}int dist[MAXN];
bool vis[MAXN];
//这里没有使用cnt数组来计算是否有正环,因为不需要,具体原因请去AcWing的题解中寻找智慧
void SPFA () { //一个平淡无奇的SPFA算法queue<int>que;memset(dist, NINF, sizeof(dist));//目标是最长路,如果图中有正环则无解que.push(0); //从实际的-1开始求单源最长路vis[0] = 1;dist[0] = 0;while (que.size()) {int t = que.front(); que.pop();vis[t] = 0;for (Rint i=head[t], j; i; i=Next[i]) {j = ver[i];if (dist[j] < dist[t] + edge[i]) {//上一行中的 < 代表我们求解的不等式形如s[a]-s[b]>=c//如果不等式形如s[a]-s[b]<=c就将 < 改为 >dist[j] = dist[t] + edge[i];if (!vis[j]) {que.push(j);vis[j] = 1;}}}}
}int main () {while (scanf("%d", &n) != EOF) {for (Rint i=1, a, b, c; i<=n; i++) {scanf("%d %d %d", &a, &b, &c);a++; b++;g_add(a-1, b, c); //不等式1//a可以取值为0,会导致a-1 = -1//所有数字被+1,来避免从 点-1 向其他点连边}for (Rint k=1; k<=50001; k++) {g_add(k-1, k, 0); //不等式2g_add(k, k-1, -1); //不等式3}SPFA();printf("%d\n", dist[50001]);//答案为s[50000],也就是查询0-50000取了几个数,而s[50000]=dist[50001]}
}
//注意:数组长度应该至少为n的3倍大,以便添加所有需要的边

已经测试,可以通过


题目 AcWing-2128 狡猾的商人 (等式转化为不等式)

AcWing-2128 狡猾的商人

刁姹接到一个任务,为税务部门调查一位商人的账本,看看账本是不是伪造的。

账本上记录了  个月以来的收入情况,其中第  个月的收入额为 

当  大于  时表示这个月盈利  元,当  小于  时表示这个月亏损  元。

所谓一段时间内的总收入,就是这段时间内每个月的收入额的总和。

刁姹的任务是秘密进行的,为了调查商人的账本,她只好跑到商人那里打工。

她趁商人不在时去偷看账本,可是她无法将账本偷出来,每次偷看账本时她都只能看某段时间内账本上记录的收入情况,并且她只能记住这段时间内的总收入。

现在,刁姹总共偷看了  次账本,当然也就记住了  段时间内的总收入,你的任务是根据记住的这些信息来判断账本是不是假的。

思路

根据分析,我们可以发现如果账本是伪造的,那么由差分约束建立的图会出现负环,具体的原因可以去AcWing的题解区寻找智慧。(啊哈,我偷懒了)

1.找找题目中的不等关系

对于每一次偷看得到的结果,有:

(S[]表示前缀和,t 表示结束时间,s 代表开始时间,v 表示从 s 到 t 的总收入)

2.把上面的等式转化成带有  的形式,形成以下两条不等式:

3.建立有向图,把不等式塞进去:

 权值为 v;

 权值为 -v;

4.依旧是跑一次最长路SPFA,看是否有负环。

本题告诉我们,差分约束除了可以处理不等式外,还可以处理等式。

代码

#include <bits/stdc++.h>
using namespace std;#define Rint register int
#define NINF 0xCF
#define MAXN 2005int n, m, w;
int s[MAXN];int head[MAXN], ver[2*MAXN], edge[2*MAXN], Next[2*MAXN], tot;
void g_add (int x, int y, int z) {ver[++tot]=y; edge[tot]=z; Next[tot]=head[x]; head[x]=tot;
}int dist[MAXN], cnt[MAXN];
//这次要计算是否出现负环了,所以加上cnt数组
bool vis[MAXN];
bool SPFA () {queue<int> que;memset(dist, NINF, sizeof(dist));memset(cnt, 0, sizeof(cnt));memset(vis, 0, sizeof(vis));que.push(0);dist[0] = 0;while (que.size()) {int x = que.front(); que.pop();vis[x] = 0;for (Rint i=head[x], y; i; i=Next[i]) {y = ver[i];if (dist[y] < dist[x] + edge[i]) {dist[y] = dist[x] + edge[i];cnt[y] = cnt[x] + 1;if (cnt[y] >= n) return 0;//没有负环的话,cnt[y]最高大概是n-1if (!vis[y]) {que.push(y);vis[y] = 0;}}}}return 1;
}int main () {scanf("%d", &w);while (w--) {scanf("%d %d", &n, &m);for (Rint i=1, x, y, z; i<=m; i++) {scanf("%d %d %d", &x, &y, &z);g_add(x-1, y, z); //不等式1g_add(y, x-1, -z); //不等式2//这两个不等式是由一个等式拆分而来的}if (SPFA()) printf("true\n");else printf("false\n");memset(head, 0, sizeof(head));tot = 0; //记得初始化}
}

已经测试,可以通过


题目 AcWing-3265 再卖菜

AcWing-3265 再卖菜

在一条街上有  个卖菜的商店,按  至  的顺序排成一排,这些商店都卖一种蔬菜。

第一天,每个商店都自己定了一个正整数的价格。

店主们希望自己的菜价和其他商店的一致,第二天,每一家商店都会根据他自己和相邻商店的价格调整自己的价格。

具体的,每家商店都会将第二天的菜价设置为自己和相邻商店第一天菜价的平均值(用去尾法取整)。

注意,编号为  的商店只有一个相邻的商店 ,编号为  的商店只有一个相邻的商店 ,其他编号为  的商店有两个相邻的商店  和 

给定第二天各个商店的菜价,可能存在不同的符合要求的第一天的菜价,请找到符合要求的第一天菜价中字典序最小的一种。

字典序大小的定义:对于两个不同的价格序列  和 (b1,b2,b3,…,bn),若存在 ,使得 ,且对于所有 ,则认为第一个序列的字典序小于第二个序列。

思路

这个题目的不等式难以寻找,找到不等式后还需某些处理。。。

1.我们现设 a[] 表示第一天,b[] 表示第二天,那么我们可以得到以下3个等式

1. 

2.

3.

外加一个特殊条件 ;因为卖价是正整数

2.转化我们写出的不等式,这里我们必须注意向下取整符号对等式的影响

1.        

2.

3.

以上的3个不等式考虑了向下取整的影响,但是仍然不是差分约束的形式

3.把我们找到的不等式变换成差分约束的形式

1.         

2.

3.

4.建立有向图,进行SPFA求最长路求解

代码

#include <bits/stdc++.h>
using namespace std;#define ll long long
#define Rint register int
#define NINF 0xCF
#define MAXN 505int n;
int a[MAXN], b[MAXN], s[MAXN];int head[MAXN], ver[2*MAXN], edge[2*MAXN], Next[2*MAXN], tot;
void g_add (int x, int y, int z) {ver[++tot]=y; edge[tot]=z; Next[tot]=head[x]; head[x]=tot;
}int dist[MAXN];
bool vis[MAXN];
void SPFA () {queue<int>que;memset(dist, NINF, sizeof(dist)); //目标是最长路que.push(0);dist[0] = 0;while (que.size()) {int t = que.front(); que.pop();vis[t] = 0;for (Rint i=head[t], j; i; i=Next[i]) {j = ver[i];if (dist[j] < dist[t] + edge[i]) { //更新为更大的权dist[j] = dist[t] + edge[i];if (!vis[j]) { //尽可能少的去尝试que.push(j);vis[j] = 1;}}}}
}int main () {while (scanf("%d", &n) != EOF) {for (Rint i=1; i<=n; i++) {scanf("%d", &b[i]);}for (Rint i=2; i<=n-1; i++) {g_add(i-2, i+1, 3*b[i]); // 1<i<n 时的情况g_add(i+1, i-2, -(3*b[i]+2));}g_add(0, 2, 2*b[1]); //当i等于1时g_add(2, 0, -(2*b[1]+1));g_add(n-2, n, 2*b[n]); //当 i 等于 n 时g_add(n, n-2, -(2*b[n]+1));for (Rint i=1; i<=n; i++) g_add(i-1, i, 1);//商人只能卖出正整数价格,故s[i-1]-s[i]>=1SPFA(); //肯定没有负环,因为这个题保证一定有解for (Rint i=1; i<=n; i++) {if (i > 1) printf(" ");printf("%d", dist[i]-dist[i-1]);}printf("\n");}
}

已经测试,可以通过


题目 AcWing-393 雇佣收银员

AcWing-363 雇佣收银员

一家超市要每天 小时营业,为了满足营业需求,需要雇佣一大批收银员。

已知不同时间段需要的收银员数量不同,为了能够雇佣尽可能少的人员,从而减少成本,这家超市的经理请你来帮忙出谋划策。

经理为你提供了一个各个时间段收银员最小需求数量的清单

表示午夜  到凌晨  的最小需求数量, 表示凌晨  到凌晨  的最小需求数量,以此类推。

一共有  个合格的申请人申请岗位,第  个申请人可以从  时刻开始连续工作  小时。

收银员之间不存在替换,一定会完整地工作  小时,收银台的数量一定足够。

现在给定你收银员的需求清单,请你计算最少需要雇佣多少名收银员。

思路

这题的转化难度是有的,让我们从寻找这个题的答案格式开始,答案应该是 0点到24点所雇佣的总人数之和(,其中a表示某时刻上岗人数)。对于某个时刻,在这个时刻前8小时内上岗的人数的总和应该大于等于这个时刻对收银员的最小需求。(如:);同时因为时间是循环的,所以这个等式也需要可循环。我们可以使用差分数组来代替8个连续的a以表示区间和。转化成如下形式:,到这一步,我们想到可以用差分约束试试。

再看看还有没有其他的条件:

1. 在每一个时刻上岗的人都不能是负数 

2. 在每一时刻上岗的人都不能超过来申请的人   (其中mem[]为某一时刻申请的总人数);

3. 同时要注意时间是循环的,所以需要特别处理:(例:)

好的,现在我们有了不等式,那么我们可以看看我们最终需要求解什么问题,同时我们也意识到在上面的 3. 中,出现了3个s[],不能直接连边。结合两个问题我们尝试将s[23]变为我们寻找的方向。因为s[23]代表了最终的答案,即雇佣的人数,也是我们可以直接“尝试”出的量之一,需要注意的是,我所说的尝试表示我们枚举答案,并通过计算其正确性的方式,完成最终答案的求解。具体请看代码。

代码

#include <bits/stdc++.h>
using namespace std;#define Rint register int
#define NINF 0xCF
#define MAXN 55
#define MAXM 2004int n, m, ans, TT;
int mem[MAXN], R[MAXM];int head[MAXM], ver[2*MAXM], edge[2*MAXM], Next[2*MAXM], tot;
void g_add (int x, int y, int z) {ver[++tot]=y; edge[tot]=z; Next[tot]=head[x]; head[x]=tot;
}int dist[MAXM], cnt[MAXM];
bool vis[MAXM];
bool SPFA () {queue<int> que;memset(dist, NINF, sizeof(dist));memset(vis, 0, sizeof(vis));memset(cnt, 0, sizeof(cnt));que.push(0);dist[0] = 0;while (que.size()) {int x = que.front(); que.pop();vis[x] = 0;for (Rint i=head[x], y; i; i=Next[i]) {y = ver[i];if (dist[y] < dist[x] + edge[i]) { //更新为更大的权dist[y] = dist[x] + edge[i];cnt[y] = cnt[x] + 1;if (cnt[y] >= 25) return 0; //0~24,总共25个点if (!vis[y]) {que.push(y);vis[y] = 0;}}}}return 1; //没有负环,说明这一种情况是正确的
}
//为什么有负环就不是正确答案:
//如果在图上 点8->点7->点6->...->点0 的权值(正数,表示需求) + 点0->点8 的权值(负数,表示供给) < 0
//那么形成了负环
//同时也就表示了在 0时到7时 招募的人数总数不够 8时 的总需求
//所以不是正确答案int main () {scanf("%d", &TT);while (TT--) {for (Rint i=1; i<=24; i++) scanf("%d", &mem[i]);scanf("%d", &m);memset(R, 0, sizeof(R));for (Rint i=1, x; i<=m; i++) {scanf("%d", &x);R[x+1]++; //记录每一个时刻有多少人可以上岗//为了防止出现读取R[-1]的情况,我们后移整个数组}bool hass = 0; //记录是否找到了答案for (ans=0; ans<=m; ans++) { //当然可以二分,请自行发掘//枚举答案,尝试雇佣0个到所有员工//我们从小到大尝试,第一种正确的情况就是最优的tot = 0;memset(head, 0, sizeof(head));g_add(0, 24, ans);g_add(24, 0, -ans);//我们要雇佣ans个员工,那么我们s[24]就应该等于ansfor (Rint i=0; i<24; i++) g_add(i, i+1, 0);//s[i+1]-s[i]>=0 毕竟我们不能雇佣数量为负数的员工(阴兵)for (Rint i=1; i<=24; i++)g_add(i, i-1, -R[i]);//s[i]-s[i-1]<=R[i] 毕竟我们每个时间可以雇佣的员工的个数最多为R[i]for (Rint i=0; i<=24; i++) {if (i <= 16) g_add(i, i+8, mem[i+8]);elseg_add(i, i-16, mem[i-16]-ans);//因为时间是连续的,24点也就是0点,所以应该是环状的}//我们要雇佣的员工个数一定得足够不是吗if (SPFA()) { //寻找是否有负环printf("%d\n", ans);hass = 1;break;}}//如果我们雇佣所有人都不能完成这件事,那么就无解if (!hass) printf("No Solution\n");}
}

已经测试,可以通过

核心算法地格式大致的统一了

[差分约束] AcWing-393 雇佣收银员 AcWing-2128 狡猾的商人 AcWing-3265 再卖菜相关推荐

  1. 【详细讲解】某书店有一个收银员该书店最多允许n个购书者进入。将收银员和购书者看作不同的进程,其工作流程如下图所示。利用PV操作实现该过程,设置信号量S1,S2和Sn,初值分别为0,0,n.则图中a1

    某书店有一个收银员该书店最多允许n个购书者进入.将收银员和购书者看作不同的进程,其工作流程如下图所示.利用PV操作实现该过程,设置信号量S1,S2和Sn,初值分别为0,0,n.则图中a1和a2应填入( ...

  2. 『设计模式』就因为多收了我2块5,我追着收银员问是不是不懂设计模式--策略模式

    23种设计模式+额外常用设计模式汇总 (持续更新) 今天去超市买东西,买了50多块钱的东西,然后收钱的时候他多收了,明明会员要打白金会员打9折,黄金会员95折,我是白金会员因该是9折. 我问她:&qu ...

  3. 收银员英文缩写_如何在没有收银员的苹果商店购买东西

    收银员英文缩写 If you visit an Apple Store in the hopes of buying a new iPhone, iPad, or MacBook, you have ...

  4. 在宜家兼职收银员创收

    十年前大学毕业之后留在了沈阳市工作,那时候和朋友特别喜欢逛宜家,就为了去吃牛肉丸和一块钱的冰淇淋!有一天发现一家张贴一个招聘兼职收银员的广告,时薪9.5元!我和朋友就报名面试了! 宜家是瑞典的企业,需 ...

  5. Callnovo与Freshii合作推出的北美餐饮业颠覆性解决方案-远程真人视频收银员

    新闻:Callnovo与Freshii合作推出了北美餐饮业颠覆性解决方案-远程真人视频收银员(Remote Cashier).颠覆性的远程真人视频收银员(Remote Cashier)解决方案在加拿大 ...

  6. 超市收银软件测试自学,pos前台收银员培训 免费超市收银软件 视频教程

    作为一个超市店铺收银员,特别是一个新手人员,在刚开始的时候,由于对收银软件不甚了解,常常在使用收银软件的时候,觉得很困惑,不知道如何下手开始.本文以星宇免费超市收银软件为例,一步步教您从安装.设置.入 ...

  7. AI收银员火了 阿里云AI产品体系首次全方位公开

    昨天,刚刚上任的AI收银员就让阿里云官抖收获了第一条10万+,嗯就是下面这个视频. 人类在点餐速度上输给了AI,面对每秒速5个字的点单需求:"五个巧克力.两个香草拿铁,巧克力加奶油--&qu ...

  8. 现在连收银员也要考试了吗?

    小编这里指的收银员是指收银员审核员哦!那么什么是收银员审核员呢?收银审核员简单来说就是核对人员,保证资金安全的人员.那跟普通的收银员有什么区别吗? 收银员指超市,商场,宾馆,酒店等经营场所给顾客结账的 ...

  9. 新手餐饮收银员应该知道的基础知识

    新手餐饮收银员基础知识如下: 1.收银员自觉遵守财经纪律和财务制度,吧台现金没有经过上级批准不准外借. 2.收银员收到点用单和酒水单后应在单据上签字并及时准确的录入电脑.点菜单的号码不要录入,以便审核 ...

最新文章

  1. linux客户端设置eth0为dhcp,如何设置Linux客户端以使用通过dhcp提供的ntp信息?
  2. 网页版python叫什么-python脚本和网页有何区别
  3. 阻塞队列 java 源码_Java源码解析阻塞队列ArrayBlockingQueue常用方法
  4. python中用函数货币转换代码_python将人民币转换大写的脚本代码
  5. Linux下遍历目录下的文件
  6. javascript中依赖属性(Dependency Property)的实现
  7. 大数据之-Hadoop3.x_MapReduce_序列化案例需求分析---大数据之hadoop3.x工作笔记0096
  8. LVS——DR模式+Keepalived(高可用)
  9. D-Link 老款路由器被曝多个高危漏洞,未完全修复
  10. Java之戳中痛点 - (6)避免类型自动转换,例如两个整数相除得浮点数遇坑
  11. KVM 介绍(2):CPU 和内存虚拟化
  12. Fluent软件零基础入门到精通教程
  13. GitHub的安装与配置
  14. MySQL命令执行脚本文件
  15. 努比亚(nubia) Z18 mini NX611J 解锁BootLoader 并刷入recovery ROOT
  16. word标题排序包括有汉字和罗马数字的标题排序
  17. docker部署minio分享图片链接ip问题
  18. 微信支付服务器商模式,微信支付分正式支持服务商模式!
  19. Java object转string
  20. Spring和SpringBoot中针对单一接口多种实现的支持

热门文章

  1. shopex 4.8.5.45144注入和远程shell写入漏洞
  2. 计算机d类事业单位题,事业单位计算机简答实务大题.doc
  3. 给我一个不坚强的理由
  4. 11.Nginx总结 嘻哈的简写笔记——Nginx
  5. 花束生长动画婚礼请柬手机视频制作PR竖屏模板MOGRT
  6. 如何禁止小白查看网页源代码的简单操作
  7. 利用Shap Value挑选变量
  8. ESP8266+电量计模块通过物联网oneNET实现家庭用电的实时监测
  9. 小程序文本框为空自动填写_218个典藏版工程量自动计算表+29个小程序,只发一次,速领...
  10. Volumetric Level Set Hair Workflow