20151008

来源:互联网 发布:威纶通触摸屏编程手册 编辑:程序博客网 时间:2024/05/16 03:41

20151008题解

番外篇:考试4道题本来认为上200+有很多人,结果老师高能,说最后一道题太简单,把小数据都删了QAQ。。

(我就是靠小数据谋生的啊),更高能的是,老师把大数据重复了两份,还把100分的题设置成了200。。。。

加上第二道题出乎意料得了70

出纳员的雇佣

【题目描述】

德黑兰的一家每天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 00 0 0 1

5

0

23

22

1

10

【样例输出】

1

 

题解

我一开始想了,觉得是个贪心,后来想想没有贪心标准,贪心时还得不断做出决策

于是想到了DP,DP觉得数据不大,应该就是动归,结果。。。。。连状态数组开几维都想不出来。其实我们在想状态数组时亦然,一定要想它表示的是什么意义,表示出的意义在程序实际运行时是否符合DP的思想。后来这个题打了个2^n的暴力,匆匆交了上去

旁边某位神犇直接输出NO Solution我也是服了,这多组测试数据。。。。。

后来这个题是考试改的四道题中倒数第二道敲代码的,很疲惫,很累,非常不愿意打

但是最后还是坚持下来了,打了2KB。

正解:

------差分约束

你肯定不相信,这个和图论有个毛关系,但是可以发现,题目是给定的某一时刻的消息

但是试图描述的却是某一时间段的信息 令dis[x]表示x时间段一共至少雇佣多少员工

也就是前缀和的形式,实在比较难想,所以看不懂的oier就忽略这一段

 

下面进入差分阶段 定义ned数组 表示第x小时至少需要多少员工,也就是题目里的R

定义hav[x]表示第x小时有多少人申请工作.边二分,边构图spfa跑最短路,

二分什么---我们二分的是lim即欲招员工的个数

对于题目中给定的约束条件构图

     (1)0<=dis[x]-dis[x-1]<=hav[x]//雇佣的人数小于等于申请的人数但不能为负数

     (2)dis[x]-dis[x-8]>=ned[x]//显然,数组下表非负,当x>=8时,此方程成立

    //对于这个方程意义的解释为,在第x小时可以干活的人只与x-8小时后的有关系

     (3)dis[x-8+24]-dis[i]<=lim-ned[x];//当x<8时,就用这个方程 你会发现样例是通宵上班的

     (4)dis[24]-dis[0]>=lim//最后24小时内雇佣的人应大于等于lim个人

这样构图得

dis[24]-dis[0]>=lim,发现lim是变量,那就二分呗,

变形得dis[0]-dis[24]<=-lim再变形得 d[0]<=d[24]-lim;

即让dis[0]最小那不就是求24到0的最短路 spfa秒之...

还要注意:我给的代码是从1~24 题目描述是从0~23

.............那就这样吧

#include<cstdio>#include<cstring>#include<queue>using namespace std;const int MAXN = 1010;int hav[MAXN],ned[MAXN],f[MAXN];bool vis[MAXN];int c[MAXN];int T,ans=-1,n;struct Edge{int next,to,data;}edge[MAXN*MAXN];int edgenum;int head[MAXN];inline void merge(int x,int y,int data){edgenum++; edge[edgenum].next=head[x];edge[edgenum].to=y; edge[edgenum].data=data; head[x]=edgenum;}inline bool spfa(int lim){queue<int> q; vis[24]=1;f[24]=0;c[24]=1;q.push(24);while(!q.empty()){int fr=q.front();q.pop();vis[fr]=0;if(c[fr]>25) return false;for(int i=head[fr];i!=-1;i=edge[i].next){int t=edge[i].to;if(f[t]>f[fr]+edge[i].data){f[t]=f[fr]+edge[i].data;if(!vis[t]){vis[t]=1;c[t]++;q.push(t);}}}}if(f[0]==-lim)return 1;return 0;}inline bool check(int lim){memset(head,-1,sizeof head);memset(f,25,sizeof f);memset(vis,0,sizeof vis);memset(c,0,sizeof c);edgenum=0;for(int i=1;i<=24;i++){        merge(i-1,i,hav[i]);         merge(i,i-1,0);               }       for(int i=1;i<8;i++)            merge(i,24+i-8,lim-ned[i]);    for(int i=8;i<=24;i++)         merge(i,i-8,-ned[i]);    merge(24,0,-lim); if(spfa(lim))return 1;return 0;}int main(void){freopen("ployment.in","r",stdin);freopen("ployment.out","w",stdout);scanf("%d",&T);while(T){memset(hav,0,sizeof hav);ans=-1;T--;for(int i=1;i<=24;i++)scanf("%d",&ned[i]);scanf("%d",&n);int x;for(int i=1;i<=n;i++){scanf("%d",&x); x++;hav[x]++;}int l=0,r=n;while(l<=r){int mid=(l+r)>>1;if(!check(mid))l=mid+1;else{ans=mid; r=mid-1;}}if(ans==-1)printf("No Solution\n");elseprintf("%d\n",ans);}return 0;}



飞行路线--堆优化spfa

Description

Alice和Bob现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在n个城市设有业务,设这些城市分别标记为0到n-1,一共有m种航线,每种航线连接两个城市,并且航线有一定的价格。Alice和Bob现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多k种航线上搭乘飞机。那么Alice和Bob这次出行最少花费多少?

Input

数据的第一行有三个整数,n,m,k,分别表示城市数,航线数和免费乘坐次数。

第二行有两个整数,s,t,分别表示他们出行的起点城市编号和终点城市编号。(0<=s,t<n)

接下来有m行,每行三个整数,a,b,c,表示存在一种航线,能从城市a到达城市b,或从城市b到达城市a,价格为c。(0<=a,b<n,a与b不相等,0<=c<=1000)

Output

只有一行,包含一个整数,为最少花费。

Sample Input

5 6 1

0 4

0 1 5

1 2 5

2 3 5

3 4 5

2 3 3

0 2 100

Sample Output

8

HINT

对于30%的数据,2<=n<=50,1<=m<=300,k=0;

对于50%的数据,2<=n<=600,1<=m<=6000,0<=k<=1;

对于100%的数据,2<=n<=10000,1<=m<=50000,0<=k<=10.

这个题..,我也是无语了,一上来就敲了个裸的二位spfa,结果得了60 QAQ(话说spfa没听说过堆优化)...

Spfa对稀疏图的确强大,但由于稠密图常数很大,和迪杰斯特拉差不多,spfa肯定跪

因此想到了堆优化

那就这样吧


#include<cstdio>#include<cstring>#include<queue>#define MAXN 100540#define MAXC 10101#define max(a,b) a>b?a:b#define min(a,b) a<b?a:busing std::priority_queue;inline int in(void){    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;}int edgenum,n,m,k;struct Edge{int next,to,data;}edge[MAXN];struct node{    int fir,sec,data;    friend bool operator < (node a,node b){return a.data>b.data;}};int head[MAXC];bool vis[MAXC][15];int f[MAXC][15];int s,t,ans=0x7fffffff;inline void add(int x,int y,int data){    edgenum++;    edge[edgenum].next=head[x];    edge[edgenum].to=y;    edge[edgenum].data=data;    head[x]=edgenum;    return ;}inline node make_node(int x,int y){    node now;    now.fir=x,now.sec=y,now.data=f[x][y];    return now;}inline void spfa(int s){    memset(f,0x3f,sizeof f);    memset(vis,0,sizeof vis);    priority_queue<node> q;    vis[s][0]=true;    f[s][0]=0;    q.push(make_node(s,0));    while(!q.empty()){        int fr_fir=q.top().fir;        int fr_sec=q.top().sec;        vis[fr_fir][fr_sec]=0;        q.pop();        for(int i=head[fr_fir];i!=-1;i=edge[i].next)            if(f[edge[i].to][fr_sec]>f[fr_fir][fr_sec]+edge[i].data){                f[edge[i].to][fr_sec]=f[fr_fir][fr_sec]+edge[i].data;                if(!vis[edge[i].to][fr_sec]){                    vis[edge[i].to][fr_sec]=1;                    q.push(make_node(edge[i].to,fr_sec));                }            }        if(fr_sec+1<=k){            for(int i=head[fr_fir];i!=-1;i=edge[i].next)                if(f[edge[i].to][fr_sec+1]>f[fr_fir][fr_sec]){                    f[edge[i].to][fr_sec+1]=f[fr_fir][fr_sec];                    if(!vis[edge[i].to][fr_sec+1]){                        vis[edge[i].to][fr_sec+1]=1;                        q.push(make_node(edge[i].to,fr_sec+1));                    }                }        }      }    return ;}int main(){ freopen("meg.in","r",stdin); freopen("meg.out","w",stdout);    memset(head,-1,sizeof head);    n=in(),m=in(),k=in(),s=in(),t=in();    for(int i=1,x,y,ch;i<=m;i++) x=in(),y=in(),ch=in(),add(x,y,ch),add(y,x,ch);    spfa(s);    for(int i=0;i<=k;i++)        ans=min(f[t][i],ans);    printf("%d",ans);    return 0;}

NOIP借教室--segment_tree| |二分答案

【题目描述】

在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。

面对海量租借教室的信息,我们自然希望编程解决这个问题。

我们需要处理接下来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。

 

数据结构大神都会想这是一个裸的线段树操作.......可是NOIP官方数据应该可以将带lazytag的线段树卡掉三个点.....CCF真是..无语了

 

先让我吐槽一下.考试先敲暴力,敲完暴力打线段树发现全跪了,因为询问区间最小值改成求和了...样例竟然可以过!!!!!!!!!

线段树就这么飘过70

 

再有就是二分答案

二分订单数量,判断一下前mid个订单是否可以。具体操作是前缀和处理,即每读入一个订单就在起始天+要借的房间数量,在结束天的下一天减去要借的房间数量。(#)

然后比较每一天的前缀和与本天总共的房间数的大小,如果房间数<前缀和,就说明前mid个订单有问题,向前二分;否则就向后二分。

注意left和right的处理

 

上代码

#include<cstdio>#include<cstring>using namespace std;#define MAXN 1001010ULint ans;int a[MAXN],sum[MAXN],d[MAXN],x[MAXN],y[MAXN];int n,m,l,r;inline bool check(int mid){    memset(sum,0,sizeof sum);    for(int i=1;i<=mid;i++){sum[x[i]]+=d[i],sum[y[i]+1]-=d[i];}    int tot=0;    for(int i=1;i<=n;i++){        tot+=sum[i];        if(tot>a[i])return false;    }    return true;}int main(){freopen("classroom.in","r",stdin);freopen("classroom.out","w",stdout);    scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++) scanf("%d",&a[i]);    for(int i=1;i<=m;i++) scanf("%d%d%d",&d[i],&x[i],&y[i]);    l=1,r=n;    while(l<=r){int mid=(l+r)>>1;        if(!check(mid)) ans=mid,r=mid-1;        else l=mid+1;}    if(!ans)        printf("0");    else printf("-1\n%d",ans);    return 0;}

每日一语 生活就是让你做一些你不情愿的事情;


0 0