20151008

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");
		else
			printf("%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:b
using 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 1001010UL
int 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;
}

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值