2015 10 08
来源:互联网 发布:html5网页小游戏源码 编辑:程序博客网 时间:2024/06/06 20:12
问题 E:出纳员的雇佣
时间限制: 1 Sec
内存限制: 128 MB
提交: 16
解决: 12
提交状态
题目描述
德黑兰的一家每天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 0 0 1
5
0
23
22
1
10
样例输出
1
题解:一眼看上去,没思路,再看,还是没思路;查查题解,竟然是差分约束,坑。。。。。。。。
共给了23个点的信息,为了好处理,向后移一位,1---24小时就可以了,d[i]表示前i个小时一共雇佣了多少出纳员,hav[i]表示在第i时刻申请雇佣的人数,ned[i]表示第i时刻至少有的人数,根据关系,可得到以下几个式子:
d[i]-d[i-1]>=0; d[i]-d[i-1]<=hav[i]; d[i]-d[i-8]>=ned[i];(i>=8)d[i-8+24]-d[i]<=lim-ned[i];(i<8)
最后 d[24]-d[0]>=lim;接下来,就是求解24--à0的最短路了;
二分答案,比较dis[0]是否等于枚举的lim,如果满足,再小点,可能更优;不满足,再大点,
顺便判断一下负环(用bellford判断就行了);
#include<cstdio>#include<cstring>#include<queue>using namespace std;int head[29],dis[29],Num,ned[29],hav[29],ans,tot,xi;int num[29],l,r,T;bool vis[29];struct dd{int begin,end,juli,next;}jie[25000];void add(int x,int y,int z){ jie[++tot].end=y; jie[tot].juli=z; jie[tot].next=head[x]; head[x]=tot;}void init(){ for(int i=1;i<=24;++i) scanf("%d",&ned[i]);scanf("%d",&Num);for(int i=1;i<=Num;++i){ scanf("%d",&xi); xi++; hav[xi]++;}}bool spfa(int x){for(int i=0;i<=24;++i) {dis[i]=99999999; vis[i]=0; num[i]=0;}queue<int>que;dis[24]=0; vis[24]=1; num[24]=1;que.push(24);while(!que.empty()){int yu=que.front(); vis[yu]=0; que.pop();if(num[yu]>25) return 0;for(int i=head[yu];i!=-1;i=jie[i].next){int lin=jie[i].end;if(dis[lin]>dis[yu]+jie[i].juli){dis[lin]=dis[yu]+jie[i].juli;if(!vis[lin]){vis[lin]=1;num[lin]++;que.push(lin);}}}}if(dis[0]==-x) return 1;return 0;}bool judge(int mid){tot=0;memset(head,-1,sizeof(head));for(int i=1;i<=24;++i){add(i,i-1,0);add(i-1,i,hav[i]);}for(int i=1;i<8;++i) add(i,i+16,mid-ned[i]);for(int i=8;i<=24;++i) add(i,i-8,-ned[i]);add(24,0,-mid);if(spfa(mid)) return true;return false;}void work(){l=0,r=Num; ans=-1;while(l<=r){int mid=(l+r)>>1;if(judge(mid)){ ans=mid; r=mid-1;}else l=mid+1;}if(ans==-1) puts("No Solution");else printf("%d\n",ans);}int main(){freopen("ployment.in","r",stdin);freopen("ployment.out","w",stdout);scanf("%d",&T);for(int op=1;op<=T;++op){ memset(hav,0,sizeof(hav)); init(); work();}}
问题 H:借教室(Day 2)
时间限制: 1 Sec
内存限制: 128 MB
提交: 37
解决: 15
提交状态
题目描述
在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。
面对海量租借教室的信息,我们自然希望编程解决这个问题。
我们需要处理接下来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开始的整数编号。
数据规模:
对于10%的数据,有1≤n, m≤10;
对于30%的数据,有1≤n, m≤1000;
对于70%的数据,有1≤n, m≤105;
对于100%的数据,有1≤n, m≤106,0≤ri, dj≤109,1≤sj≤tj≤n。
输出
如果所有订单均可满足,则输出只有一行,包含一个整数0。否则(订单无法完全满足)输出两行,第一行输出一个负整数-1,第二行输出需要修改订单的申请人编号。
下面是对样例数据的解释:
第1份订单满足后,4天剩余的教室数分别为0,3,2,3。第2份订单要求第2天到第4天每天提供3个教室,而第3天剩余的教室数为2,因此无法满足。分配停止,通知第2个申请人修改订单。
样例输入
4 3
2 5 4 3
2 1 3
3 2 4
4 2 4
样例输出
-1
2
题解;
好题啊,坑死我了;
线段树无延迟标记本可以过几个点,可某某老师竟然删掉了小测试点,把大测试点复制了两遍,分差拉的老大了。。废话的不说了,上题解;
解法1:
线段树70分,裸的线段树操作,加上延迟标记,95分;
解法2:
二分订单数量,判断一下前mid个订单是否可以。具体操作是前缀和处理,即每读入一个订单就在起始天+要借的房间数量,在结束天的下一天减去要借的房间数量。(#)
然后比较每一天的前缀和与本天总共的房间数的大小,如果房间数<前缀和,就说明前mid个订单有问题,向前二分;否则就向后二分。
(#)证明:在一个订单的起始天+要借的房间数量,在结束天的下一天减去要借的房间数量。设一个数组c[i],记录前缀和。读入的数据是d,s,t C[s]:=c[s]+d;c[t+1]:=c[t+1]-d;
那么如果第i天在s和t之间,那么前i天的sum{c[i]}中有c[s],相当于已经记下第i天的订单数量了。如果第i天在t之后,前i天的sum{c[i]}中有c[s]和c[t],因为c[s]+d+c[t+1]-d=c[s]+c[t],所以这个订单只对s和t中间天数起作用。得证
正解二分。。。似乎是这样的
用某一天的前缀和表示该天需要的教室数
比如一开始数列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 2 0
这样就实现了增加3-5需要的教室数
然后我们二分订单数
处理前mid个订单看看是否有某一天的前缀和大于di
#include<cstdio>#include<cstring>using namespace std;int b[1000005],a[1000005],A[1000006],B[1000006],C[1000006],n,m,l,r,ans,sum;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*10+c-'0';return x;}bool judge(int x){sum=0;memset(b,0,sizeof(b));for(int i=1;i<=x;++i) b[B[i]]+=A[i],b[C[i]+1]-=A[i];for(int i=1;i<=n;++i) {sum+=b[i]; if(sum>a[i]) return 0;}return 1;}int main(){ freopen("classroom.in","r",stdin);freopen("classroom.out","w",stdout);n=in(); m=in();for(int i=1;i<=n;++i) a[i]=in();for(int i=1;i<=m;++i){A[i]=in(); B[i]=in(); C[i]=in();}l=1;r=m;while(l<=r){ int mid=(l+r)>>1; if(!judge(mid)){ans=mid; r=mid-1; } else l=mid+1;}if(!ans) puts("0");else {printf("-1\n%d",ans);}}
- [2015-08-10] python021
- 2015-10-08
- 2015-10-08 开篇
- 2015 10 08
- 2015-08-10-----2015-08-11
- java培训 2015-08-10
- 2015-10-08 国庆归来
- 2015年08月10日
- [2015-08-08] python019
- hdoj.1296 Polynomial Problem【水题】 2015/08/10
- Android 开发学习记录一 08-10-2015
- 【练习册】 2015-08-10 ClassicTrie by python
- ms CRM 08/10/2015 - XRMtoobox的view function.
- 08-10-08-胶着状态
- 10-08
- 10-08
- 2015/02/08
- 08-04-2015 Recursion
- 两个数组求和为一个固定数值的所有组合java实现
- 左右滑动cell,调出菜单
- Oracle Flashback技术之Flashback Database
- gradle--第十五章 使用文件2
- Java环境配置之javac不是内部或外部命令
- 2015 10 08
- 2015 10 09
- 分布式CAP理论的理解.
- SQL Server 连接字符串和身份验证
- stm32使用超高精度的同步时钟的设计
- GoF 种设计模式
- 浅谈方法重写 super
- 面试10大算法汇总以及常见题目解答
- spring mvc拦截器和<mvc:annotation-driven />的详解