Acwing 393. 雇佣收银员
算法1: 差分约束 + 枚举 O(Tn2028)
由于牵扯到 \([i - 8 + 1, i]\) 这段区间的和的约束,所以用前缀和更好表达一些。
设 \(num[i]\)表示 \(i\) 时刻有多少人申请上岗, \(x[i]\) 为 \(i\) 时刻实际上岗的人数 ,\(s\) 为 \(x\) 的前缀和数组。
则应该满足的约束条件是:
上岗人数不能负数,即 \(s[i] - s[i - 1] >= 0\)
实际上岗人数不能超过申请人数,即 \(s[i] - s[i - 1] <= num[i]\)
\(i\) 时刻所在人数,即 \([i - 7, i]\) 区间内的上岗人数要大于等于最小需求 \(R\)
由于存在环,即 \(23\) 到 \(24\),再到 \(0\) 时刻,所以要分类讨论:
- 当 \(i >= 8\) 时,\(s[i] - s[i - 8] >= R[i]\)
- 当 \(i <= 7\) 时,\(s[i] + s[24] - s[24 - i] >= R[i]\)
显然这是一个明显的差分约束问题,由于求最小人数,所以用最长路转化:
- \(s[i] >= s[i - 1]\) 即 \(add(i - 1, i, 0)\)
- \(s[i] - num[i] <= s[i - 1]\) 即 \(add(i, i - 1, -num[i])\)
- \(s[i - 8] + R[i] <= s[i]\) 即 \(add(i - 8, i, R[i])\)
- \(s[16 + i] + R[i] - s[24] <= s[i]\),不会连边了hhhh
最后一种约束关系我们不会连边的原因无非是出现了三个变量,但我们可以发现:
- 所有最后一种约束关系都有 \(s[24]\) 变量,其实这个东西就是我们求的答案,所以我们可以枚举 \(s[24]\) 的值,把它变成常量就行啦!然后就可以 \(add(16 + i, i, R[i] - s[24])\)
\(Tips:\)
关于建图,其实可以在线建图,不用僵化建边了嘿嘿。
发现 \(0\) 肯定所有点,所以不用创造超级源点了,只需从 \(0\) 点出发跑最短路即可。
不要忘了 $s[24] = $ 我们枚举的数 \(c\)(要严格等于,实现是 大于等于 + 小于等于):
- \(s[24] <= c\) 即 \(add(24, 0, -c)\)
- \(s[24] >= c\) 即 \(add(0, 24, c)\)
时间复杂度
这个题中的点数 $ <= 26$,边数 $ <= 26 * 3 = 78$,
所以时间复杂度 \(O(Tn2028)\),足以 \(AC\)
\(5ms\) 可还行
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N = 25;
int n, ans, cnt[N], dis[N], R[N], num[N];
int tt, q[N];
bool st[N];
/*
最长路
0 <= Si - S(i - 1)
*/
//把边 (u, v, w) 松弛
bool inline upd(int u, int v, int w) {
if(dis[u] + w > dis[v]) {
dis[v] = dis[u] + w;
cnt[v] = cnt[u] + 1;
if(cnt[v] >= 25) return false;
if(!st[v]) q[++tt] = v, st[v] = true;
}
return true;
}
// 返回是否存在可行解
bool spfa() {
memset(dis, -0x3f, sizeof dis);
memset(st, false, sizeof st);
memset(cnt, 0, sizeof cnt);
// 数组模拟栈 更容易找到环
tt = 0;
q[++tt] = 0; dis[0] = 0;
while(tt) {
int u = q[tt--];
st[u] = false;
// 严格保证 s[24] = ans
if(u == 0 && !upd(0, 24, ans)) return false;
if(u == 24 && !upd(24, 0, -ans)) return false;
// s[i] - s[i - 1] >= 0
if(u < 24 && !upd(u, u + 1, 0)) return false;
// s[i] - s[i - 1] <= num[i]
if(u > 0 && !upd(u, u - 1, -num[u])) return false;
//s[i] - s[i - 8] >= R[i]
if(u <= 16 && !upd(u, u + 8, R[u + 8])) return false;
// s[i] + s[24] - s[24 - i] >= R[i]
if(u >= 17 && !upd(u, u - 16, R[u - 16] - ans)) return false;
}
return true;
}
int main() {
int T; scanf("%d", &T);
while(T--) {
memset(num, 0, sizeof num);
for (int i = 1; i < N; i++) scanf("%d", R + i);
scanf("%d", &n);
for (int i = 1, x; i <= n; i++)
scanf("%d", &x), x++, num[x]++;
bool ok = false;
// 枚举 s24, s24 就是 答案
for (ans = 0; ans <= n; ans++) {
if(spfa()) {
printf("%d\n", ans);
ok = true;
break;
}
}
if(!ok) puts("No Solution");
}
return 0;
}
算法2: 差分约束 + 二分 O(T2028logN)
显然,答案具有单调性(若允许上岗的人越多,越容易满足条件)。
所以可以二分答案 \(LOL\)。
\(3ms\) 可还行
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N = 25;
int n, cnt[N], dis[N], R[N], num[N];
int tt, q[N];
bool st[N];
/*
最长路
0 <= Si - S(i - 1)
*/
//把边 (u, v, w) 松弛
bool inline upd(int u, int v, int w) {
if(dis[u] + w > dis[v]) {
dis[v] = dis[u] + w;
cnt[v] = cnt[u] + 1;
if(cnt[v] >= 25) return false;
if(!st[v]) q[++tt] = v, st[v] = true;
}
return true;
}
// 返回是否存在可行解
bool spfa(int s24) {
memset(dis, -0x3f, sizeof dis);
memset(st, false, sizeof st);
memset(cnt, 0, sizeof cnt);
// 数组模拟栈 更容易找到环
tt = 0;
q[++tt] = 0; dis[0] = 0;
while(tt) {
int u = q[tt--];
st[u] = false;
// 严格保证 s[24] = s24
if(u == 0 && !upd(0, 24, s24)) return false;
if(u == 24 && !upd(24, 0, -s24)) return false;
// s[i] - s[i - 1] >= 0
if(u < 24 && !upd(u, u + 1, 0)) return false;
// s[i] - s[i - 1] <= num[i]
if(u > 0 && !upd(u, u - 1, -num[u])) return false;
//s[i] - s[i - 8] >= R[i]
if(u <= 16 && !upd(u, u + 8, R[u + 8])) return false;
// s[i] + s[24] - s[24 - i] >= R[i]
if(u >= 17 && !upd(u, u - 16, R[u - 16] - s24)) return false;
}
return true;
}
int main() {
int T; scanf("%d", &T);
while(T--) {
memset(num, 0, sizeof num);
for (int i = 1; i < N; i++) scanf("%d", R + i);
scanf("%d", &n);
for (int i = 1, x; i <= n; i++)
scanf("%d", &x), x++, num[x]++;
int l = 0, r = n;
while(l < r) {
int mid = (l + r) >> 1;
if(spfa(mid)) r = mid;
else l = mid + 1;
}
if(spfa(r)) printf("%d\n", r);
else puts("No Solution");
}
return 0;
}
Acwing 393. 雇佣收银员的更多相关文章
- 湖南附中模拟day1 收银员
4.1 题意描述花花家的超市是 24 小时营业的,现在需要招聘收银员.超市每个小时都需要不同数量的收银员,用 ai 表示一天中 i 点到 i + 1 点这一小时内需要的收银员数量,特别地 a23 表示 ...
- poj1275收银员——差分约束
题目:http://poj.org/problem?id=1275 做的第一道差分约束题... 首先,根据题意得出一些不等关系(f为前缀和雇佣人数): 0 <= f[i] - f[i-1] &l ...
- 【NOIP模拟赛】收银员(一道差分约束好题)
/* s[]表示最优方案的序列中的前缀和,那么s[23]就是最优方案 由题意我们可以列出这样一些式子: s[i]+s[23]-s[16+i]>=a[i] (i-8<0) s[i]-s[i- ...
- 移动零售批发行业新的技术特色-智能PDA手持移动扫描打印销售开单收银仪!!
提起便利店或者超市,大家的第一印象一定是前台那个笨重的POS机和站在POS机后的收银员.传统的零售店中,笨重的POS机随处可见. 变革前,零售盘点多烦忧 一个顾客要结账,就需要通过POS机.小票打印机 ...
- C++ 大作业 超市收银系统
#include<iostream> #include<fstream> #include<string> #include<iomanip> #inc ...
- 互联网+下PDA移动智能手持POS超市收银开单软件
是一套专为中小超市.专卖店设计的收银管理软件,广泛应用于中小超市(百货商店).化妆品店.婴幼儿用品店.玩具店.保健品店.茶叶店. 电器.文具图书.手机通讯器材店等行业的中小型店面店铺.该系统具有完善的 ...
- PDA 收银系统PDA手持打印扫描枪 销售开单 收银 扫描打印一体机
在零售方面也有很好的应用.如在一些高端品牌零售店,营业员可以随身导购,一站式完成了商品销售和收银,很是受消费者追捧,符合了企业对客户体验以及行业领先的追求. PDA收银系统是一款多功能可以取代专业收银 ...
- 浩瀚移动POS收银开单扫描解决方案PDA仓储系统,无线批发,移动批发,无线POS,无线销售APP-车销管理PDA
适用范围 各种业态的批发商铺.批发市场.订货会.展销会.配送中心仓库…… 产品简介 随着移动技术与智能PDA设备的迅猛发展,中国已经跨步进入移动信息化社会.移动商务是移动信息社会的重要载体与形式,它开 ...
- PDA移动POS开单扫描打票收银系统-带来零售批发 新的技术 新的手段!!
手持POS终端高清彩屏,清晰.美观.大方,适用于仓库.超市.服装.食品.批发零售.手机电脑等企业管理.可与管理软件灵活对接.1:员工记不住价格,产品名称,只要有PDA扫描,价格,库存,直接开销售单,打 ...
随机推荐
- Ceph删除OSD上一个异常object
前言 ceph里面的数据是以对象的形式存储在OSD当中的,有的时候因为磁盘的损坏或者其它的一些特殊情况,会引起集群当中的某一个对象的异常,那么我们需要对这个对象进行处理 在对象损坏的情况下,启动OSD ...
- 协程实现socket并发编程
在python中多线程其实是被弱化了,因为由于GIL的原因,同一时间只有一个线程能访问CPU,即使你的CPU是多核的在python中因为多线程被弱化了,所以协程显的重要,能够在一个线程中提高CPU的利 ...
- 进程池与回调函数与正则表达式和re爬虫例子
# 使用进程池的进程爬取网页内容,使用回调函数处理数据,用到了正则表达式和re模块 import re from urllib.request import urlopen from multipro ...
- 最全Linux搭建SVN服务端教程
文章首推 支付宝接口对接 高德地图调用 验证码登录 QQ邮箱登录 今日主题:Linux搭建SVN服务端 简介 相信程序员对SVN还是不陌生的,虽然现在用Git比较多,但是SVN也是用的,SVN可以做代 ...
- tp5获取当前域名
//头部引入 use think\Request; //获取当前域名 $request = Request::instance(); $domain = $request->domain();
- TestLink在线Excel用例转换xml
[原文链接]:https://blog.tecchen.tech ,博文同步发布到博客园. 由于精力有限,对文章的更新可能不能及时同步,请点击上面的原文链接访问最新内容. 欢迎访问我的个人网站:htt ...
- 「CSP-S 2019」括号树
[题目描述] 传送门 [题解] 是时候讨论一下我在考场上是怎么将这道题写挂的了 初看这道题毫无思路,先看看部分分吧 一条链的情况?设k[i]表示前i个括号的方案数 显然\(k[i]=k[i-1]+\) ...
- mac搭建mnmp环境
brew安装nginx brew install nginx 安装php56 brew tap homebrew/dupes brew tap josegonzalez/homebrew-php br ...
- 我给 Apache 顶级项目提了个 Bug
这篇文章记录了给 Apache 顶级项目 - 分库分表中间件 ShardingSphere 提交 Bug 的历程. 说实话,这是一次比较曲折的 Bug 跟踪之旅.10月28日,我们在 GitHub 上 ...
- Linux 学习笔记04丨Linux的用户和用户组管理
Chapter 3. 用户和用户组管理 由于Linux系统是一个多用户多任务的分时操作系统,任何一个要使用系统资源的用户,都必须首先向系统管理员申请一个账号,然后以该账号身份进入系统. 3.0 用户与 ...