出纳员的雇佣
【题目描述】
德黑兰的一家每天24小时营业的超市,需要一批出纳员来满足它的需要。超市经理雇佣你来帮他解决他的问题——超市在每天的不同时段需要不同数目的出纳员(例如:午夜时只需一小批,而下午则需要很多)来为顾客提供优质服务。他希望雇佣最少数目的出纳员。
经理已经提供你一天的每一小时需要出纳员的最少数量——R0, R1, ..., R23。R0表示从午夜到上午1:00需要出纳员的最少数目,R1表示上午1:00到2:00之间需要的,等等。每一天,这些数据都是相同的。有N人申请这项工作,每个申请者I在没24小时中,从一个特定的时刻开始连续工作恰好8小时,定义tI(0≤tI≤23)为上面提到的开始时刻。也就是说,如果第I个申请者被录取,他(她)将从tI时刻开始连续工作8小时。
你将编写一个程序,输入RI(I = 0..23)和tI (I = 1..N),它们都是非负整数,计算为满足上述限制需要雇佣的最少出纳员数目。在每一时刻可以有比对应的RI更多的出纳员在工作。
【输入格式】
输入文件的第一行为测试点个数(<= 20)。每组测试数据的第一行为24个整数表示R0,R1,..., R23(RI≤1000)。接下来一行是N,表示申请者数目(0≤N≤1000),接下来每行包含一个整数tI(0≤tI≤23)。两组测试数据之间没有空行。
【输出格式】
对于每个测试点,输出只有一行,包含一个整数,表示需要出纳员的最少数目。如果无解,你应当输出“No Solution”。
【样例输入】
1
1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 1
5
0
23
22
1
10
【样例输出】
1
problem a
Description
一次考试共有n个人参加,第i个人说:“有ai个人分数比我高,bi个人分数比我低。”问最少有几个人没有说真话(可能有相同的分数)
Input
第一行一个整数n,接下来n行每行两个整数,第i+1行的两个整数分别代表ai、bi
Output
一个整数,表示最少有几个人说谎
Sample Input
3
2 0
0 2
2 2
Sample Output
1
HINT
100%的数据满足: 1≤n≤100000 0≤ai、bi≤n
借教室
【题目描述】
在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。
面对海量租借教室的信息,我们自然希望编程解决这个问题。
我们需要处理接下来n天的借教室信息,其中第i天学校有ri个教室可供租借。共有m份 订单,每份订单用三个正整数描述,分别为dj,sj,tj,表示某租借者需要从第sj天到第tj天租 借教室(包括第sj天和第tj天),每天需要租借dj个教室。
我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供dj个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。
借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申 请人修改订单。这里的无法满足指从第sj天到第tj天中有至少一天剩余的教室数量不足dj个。
现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改订单。
【输入格式】
第一行包含两个正整数n,m,表示天数和订单的数量。
第二行包含n个正整数,其中第i个数为ri,表示第i天可用于租借的教室数量。
接下来有m行,每行包含三个正整数dj,sj,tj,表示租借的数量,租借开始、结束分别在 第几天。
每行相邻的两个数之间均用一个空格隔开。天数与订单均用从1开始的整数编号。
【输出格式】
如果所有订单均可满足,则输出只有一行,包含一个整数 0。否则(订单无法完全满足) 输出两行,第一行输出一个负整数-1,第二行输出需要修改订单的申请人编号。
【样例输入】
4 3
2 5 4 3
2 1 3
3 2 4
4 2 4
【样例输出】
-1
2
【输入输出样例说明】
第 1 份订单满足后,4 天剩余的教室数分别为 0,3,2,3。第 2 份订单要求第 2 天到 第 4 天每天提供 3 个教室,而第 3 天剩余的教室数为 2,因此无法满足。分配停止,通知第 2 个申请人修改订单。
【数据范围】
对于 10%的数据,有1 ≤ n,m ≤ 10;
对于 30%的数据,有1 ≤ n,m ≤ 1000;
对于 70%的数据,有1 ≤ n,m ≤ 10^5;
对于 100%的数据,有1 ≤ n,m ≤ 10^6,0 ≤ ri,dj ≤ 10^9,1 ≤ sj ≤ tj ≤ n。
T1 出纳员的雇佣
二分答案 + spfa判断 +差分约束
考试的时候完全没有思路,想到了DFS,但是又不知道怎么搜,然后又想到最短路覆盖,还是不会打,于是0分了。
设dis[i]表示前i个时刻工作的人数 ned[i]表示i时刻至少有多少个人工作hav[i]表示i时刻我们可以雇佣到多少人工作 lim表示想要招募的最少的员工个数,可以得到几个方程:
0<= d[1] <= ned[1];//显然刚开始不可能雇用超过ned[1]的人要不然不是最优解
0<=d[i] – d[i-1]<=hav[i]//i时刻雇佣的工作人员不大于申请总人数但不小于0
d[i]-d[i-8]>=ned[i]//i>=8时成立这是当8<=i<=24时
d[i-8+24]-d[i]<=lim-ned[i]//i<8时前一天雇佣的人还可以上班,因此可以反面处理
d[24]-d[0]>=lim//二十四小时内雇佣的人数不小于lim
然而lim是一个变量,应用以上模型只能判断一个lim是否合法,而不能找出最优解,spfa判定d[0] ==lim?之后二分答案求解
T2 problem a
DP+ 递推
石家庄集训期中考试题……当时就不会,现在还不会,考试输出rand()%10000也是醉了
设f[i]表示名次区间取到右端点时最多的说真话人数,那么n-f[n]就是最少的说假话人数
读入a表示比他分数高的人,那么他名次左端点就是a+1,读入b表示比他分数低的人数,那么他名次最低也就是n-b,得到一个闭区间[a+1,n-b],用map记录下出现的次数,用vector存可行的决策,也就是当这个闭区间出现第一次的时候保留这个区间。
f[i]=max(f[i],f[j-1]+sum[j][i])sum[j][i]表示[j,i]区间的人数
T3 借教室
解法一:线段树(COGS极限数据被卡一个点95分,老师的数据卡两个点90分)
裸的操作,读入一个区间先判断区间内的最小值是否小于需要的数量,如果小于就直接输出该操作编号即可,否则区间修改即可,切记查询时的ans要开局部变量(之后更新会覆盖全局变量值),lazy标记只修改单点即可,因为只记录最小值。
解法二:二分查找
用某一天的前缀和表示该天需要的教室数
比如一开始数列a是0 0 0 0 0 0前缀和0 0 0 0 0 0
3到5需要2的教室
将a[3]+=2,a[6]-=2
数列变为0 0 2 0 0-2
前缀和变为0 0 2 2 20
这样就实现了增加3-5需要的教室数
然后我们二分订单数
处理前mid个订单看看是否有某一天的前缀和大于di 很巧妙,然而COGS上线段树快读可以卡过最后一个点然并卵
CODE
T1
# include <cstdio>
# include <iostream>
# include <cstring>
# include <queue>
using namespace std;
int T,n,mid,tot,ans;
int hav[30],ned[30];
int dis[30],sum[30],h[30];
bool vis[30];
struct Edge {
int v,w,next;
} e[10005];
void clr() {
memset(hav,0,sizeof(hav));
memset(ned,0,sizeof(ned));
}
void add(int u,int v,int w) {
e[++tot].v = v;
e[tot].w = w;
e[tot].next = h[u];
h[u] = tot;
}
void build(int lim) {
tot = 0;
memset(h,0,sizeof(h));
for(int i = 1; i <= 24; ++i) {
add(i - 1,i,hav[i]);
add(i,i - 1,0);
}
for(int i = 1; i < 8; ++i) {
add(i,i + 24 - 8,lim - ned[i]);
}
for(int i = 8; i <= 24; ++i) {
add(i,i - 8,-ned[i]);
}
add(24,0,-lim);
}
bool check(int lim) {
queue <int> q;
memset(vis,0,sizeof(vis));
memset(dis,0x3f,sizeof(dis));
memset(sum,0,sizeof(sum));
dis[24] = 0;
vis[24] = 1;
sum[24] = 1;
q.push(24);
while(!q.empty()) {
int u = q.front();
q.pop();
vis[u] = 0;
for(int i = h[u]; i; i = e[i].next) {
int v = e[i].v;
if(dis[v] > dis[u] + e[i].w) {
dis[v] = dis[u] + e[i].w;
if(!vis[v]) {
q.push(v);
vis[v] = 1;
if(++sum[v] > 25) {
return 0;
}
}
}
}
}
if(dis[0] == -lim) return 1;
return 0;
}
bool divide(int l,int r) {
ans = -1;
while(l <= r) {
mid = (l + r) >> 1;
build(mid);
if(check(mid)) ans = mid,r = mid - 1;
else l = mid + 1;
}
if(ans == -1) return 0;
return 1;
}
void init() {
int pos;
for(int i = 1; i <= 24; ++i) scanf("%d",&ned[i]);
scanf("%d",&n);
for(int i = 1; i <= n; ++i) {
scanf("%d",&pos);
hav[pos + 1]++;
}
if(!divide(0,n))
printf("No Solution\n");
else printf("%d\n",ans);
}
int main() {
freopen("ployment.in","r",stdin);
freopen("ployment.out","w",stdout);
scanf("%d",&T);
while(T--) {
clr();
init();
}
}
T2
# define MAXN 100005
# include <cstdio>
# include <cstring>
# include <iostream>
# include <vector>
# include <map>
using namespace std;
int n;
int f[MAXN];
vector <int> c[MAXN];
map < pair<int,int>,int > m;
//dp[i] = max{dp[j-1]+sum[j][i]}
void init() {
int l,r,a,b;
scanf("%d",&n);
for(int i = 1;i <= n;++i) {
scanf("%d%d",&a,&b);
l = a + 1;
r = n - b;
if(l > r) continue;
m[make_pair(l,r)]++;
if(m[make_pair(l,r)] == 1) c[r].push_back(l);
}
for(int i = 1;i <= n;++i) {//i youqujian
f[i] = f[i - 1];
for(int j = 0;j < c[i].size();++j) {
f[i] = max(f[i],(f[c[i][j] - 1] + min(i - c[i][j] + 1,m[make_pair(c[i][j],i)])));
}
}
printf("%d",n - f[n]);
}
int main() {
freopen("a.in","r",stdin);
freopen("a.ans","w",stdout);
init();
}
T3线段树
# define lson l,mid,rt<<1
# define rson mid+1,r,rt<<1|1
# include <cstdio>
# include <cstring>
using namespace std;
const int MAXN = 1500000;
int n,m;
int tree[MAXN];
int add[MAXN];
inline int Min(int a,int b) {
if(a < b) return a;
return b;
}
inline int in(){
char c=getchar();
int x=0;
while(c<'0'||c>'9')c=getchar();
for(;c>='0'&&c<='9';c=getchar())x=(x<<3)+(x<<1)+c-'0';
return x;
}
void build(int l,int r,int rt) {
if(l == r) {
tree[rt] = in();
return ;
}
int mid = (l + r) >> 1;
int temp = rt << 1;
build(lson);
build(rson);
tree[rt] = Min(tree[temp],tree[temp | 1]);
return ;
}
inline void clear(int rt) {
if(add[rt] == 0)
return ;
int temp = rt << 1;
tree[temp] += add[rt];
tree[temp | 1] += add[rt];
add[temp] += add[rt];
add[temp | 1] += add[rt];
add[rt] = 0;
tree[rt] = Min(tree[temp],tree[temp | 1]);
return ;
}
void update(int pos_l,int pos_r,int new_date,int l,int r,int rt) {
if(pos_l <= l && pos_r >= r) {
tree[rt] += new_date;
add[rt] += new_date;
return ;
}
clear(rt);
int mid = (l + r) >> 1;
int temp = rt << 1;
if(pos_l <= mid)
update(pos_l,pos_r,new_date,lson);
if(pos_r > mid)
update(pos_l,pos_r,new_date,rson);
tree[rt] = Min(tree[temp],tree[temp | 1]);
return ;
}
int query(int pos_l,int pos_r,int l,int r,int rt) {
if(pos_l <= l && pos_r >= r)
return tree[rt];
clear(rt);
int mid = (l + r) >> 1;
int temp = rt << 1;
int ans = 0x3fffffff;
if(pos_l <= mid)
ans = query(pos_l,pos_r,lson);
if(pos_r > mid)
ans = Min(ans,query(pos_l,pos_r,rson));
return ans;
}
int main() {
freopen("classroom.in","r",stdin);
freopen("classroom.out","w",stdout);
int i,u,v,w;
n = in();
m = in();
build(1,n,1);
for(i = 1; i <= m; ++i) {
w = in();
u = in();
v = in();
if(query(u,v,1,n,1) < w) {
printf("-1\n%d",i);
return 0;
}
update(u,v,-w,1,n,1);
}
puts("0");
return 0;
}
T3 二分
# define MAXN 1000005
# include <cstdio>
# include <cstring>
using namespace std;
typedef long long LL;
int n,m,l,r,mid,ans;
int d[MAXN],hav[MAXN];
int s[MAXN];
int t[MAXN];
LL sum[MAXN];
void init(){
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;++i) scanf("%d",&hav[i]);
for(int i = 1;i <= m;++i) scanf("%d%d%d",&d[i],&s[i],&t[i]);
}
bool check(int x) {
memset(sum,0,sizeof(sum));
for(int i = 1;i <= x;++i) {
sum[s[i]] += d[i];
sum[t[i] + 1] -= d[i];
}
long long a = 0;
for(int i = 1;i <= n;++i) {
sum[i] += sum[i - 1];
if(sum[i] > hav[i]) return 0;
}
return 1;
}
void divide() {
l = 1,r = m;
while(l <= r) {
mid = (l + r) >> 1;
if(check(mid)) l = mid + 1;
else r = mid - 1;
}
if(r == m) {
printf("0");
}
else {
printf("-1\n%d",l);
}
return ;
}
int main() {
freopen("classroom.in","r",stdin);
freopen("classroom.out","w",stdout);
init();
divide();
}