2018年12月15日考试题

第一题:测量温度(temperature)
时限1s,内存256MB
【题目描述】某国进行了连续N(1<=N<=1000000)天的温度测量,测量存在误差,测量结果是第i天温度在[ l i , r i l_i,r_i ]范围内。其中 1 0 9 &lt; l i &lt; = r i &lt; = 1 0 9 -10^9&lt;l_i&lt;=r_i&lt;=10^9
求最长的连续的一段,满足该段内可能温度不降。
输入格式:
第一行一个整数n。
接下来n行,每一行两个整数,表示 l i l_i r i r_i
输出格式:
接下来输出一个整数,表示答案。
输入样例:
6
6 10
1 5
4 8
2 5
6 8
3 5
输出样例:
4

题目分析:
对于一段连续的温度不降的区间,一定满足一个条件:区间中每一天的温度上限一定不低于前面任意一天的温度下限。这启发我们,要记录前面温度的下限的最大值maxz,并将maxz与今天的温度上限比较,如果maxz大于今天的温度上限,那么maxz作废,要在后面继续找温度下限的新的最大值maxz’,直到maxz’小于等于今天的温度上限。那么maxz‘所在的区间的长度,即为当前温度不降区间的长度。然后在所有不降区间中找出最大的长度。为了在最大值作废以后更快的找到新的最大值,我们使用一个单调递减的队列来维护。如何当前不降区间的左端点是哪个呢?是队头元素吗?不是的。因为队头元素前面可能还有属于当前温度不降区间的日子。正确的左端点应该是上一个队头元素(它刚刚被弹出队列)的后一天。附代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
#define MAXN 1000005
int high[MAXN],low[MAXN],n,minz,ans,len,lastr;
int que[MAXN],f[MAXN],head,tail;//维护下界的递减单调队列
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&low[i],&high[i]);
    len=ans=1; //len为当前不降区间的长度,ans为所有不降区间的长度的最大值。
    que[tail++]=1;
    for(int i=2;i<=n;i++)
    {
        while(head<tail&&low[que[head]]>high[i])head++;
        if(head==tail)len=1;
        else len=i-que[head-1];
        while(tail>head&&low[i]>=low[que[tail-1]])tail--;
        que[tail++]=i;
        if(len>ans)ans=len;
    }
    printf("%d\n",ans);
    return 0;

第二题:奶牛慢跑(cowjog)
有n(n<=100000)头奶牛在一个无穷长的小道上慢跑。每头奶牛的起点不同,速度也不同。小道可以被分成多条跑道。奶牛只能在属于自己的跑道上慢跑,不允许更换跑道,也不允许改变速度。如果要慢跑t(t<=1000000000)分钟,要保证在任何时候不会有同一跑道上的奶牛相遇,请问最少需要多少条跑道。奶牛开始在哪条跑道是可以随意设置的。
输入格式:第一行两个整数n,t。
接下来的n行,每行包含两个整数,表示奶牛的位置和速度。位置是非负整数,速度是正整数。所有的奶牛的起点都不相同,按起点递增的顺序给出。
输出格式:
最少的跑道数。
输入样例:
5 3
0 1
1 2
2 3
3 2
6 1
输出样例:
3

题目分析:如果两头奶牛在开始和结束时刻它们的相对位置交换了或者在终点相遇了,那么它们必须是位于两根跑道上。所以,我们以奶牛开始的位置从左到右给它们编号为1~n,那么结束时,再来看它们的编号。此时,递减的编号序列都需要不同的跑道。所以,最长下降子序列的长度即是最少需要的跑道数量。
所以本题就是一个求最长下降 (不上升)子序列的题。但是,朴素的dp是 O ( N 2 ) O(N^2) 的。我们需要用一点技巧来优化一下。
在终点时刻,从左到右依次读取奶牛的编号,并将编号放入一个单调递减的数组中。具体做法为:如果当前编号cowi小于数组中最后一个元素的值 c o w j cowj ,则将 c o w i cowi 加到数组末尾;否则,对数组进行二分查找,找到第一个小于 c o w i cowi 的元素 c o w k cowk ,将 c o w k cowk 替换为 c o w i cowi 。最后数组中的元素个数即为最长不上升子序列的长度。这个做法是 O ( N l o g N ) O(NlogN) 的。该单调数组的意义可以这样理解:arr[i]表示一个奶牛的编号,该编号为所有下上升子序列中排第i位的编号的最大值。
附代码:

#include<cstdio>
int n,t,x,y,len;
long long pos,a[100005];
int main()
{
	freopen("cowjog.in","r",stdin);
	freopen("cowjog.out","w",stdout);
	scanf("%d%d",&n,&t);
	a[0]=1ll<<60;
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&x,&y);
		pos=x+1ll*y*t;
		if(pos<=a[len]) {a[++len]=pos;continue;}
		int l=0,r=len,mid;
		while(l<r)
		{
			mid=(l+r+1)/2;
			if(pos>a[mid]) r=mid-1;
			else l=mid;
		}
		a[l+1]=pos;
	}
	printf("%d",len);
}

第三题:
第三题:路径规划(path)
有n个点,m条无向边,有A,B两个人,初始时刻A在点1,B在点2,他们要走到点n去。A每走一条边,要消耗B单位能量,B每走一条边,要消耗E单位能量。如果A,B相伴走,则只消耗P单位的能量。请问A,B走到点n,最少要消耗多少能量?
输入数据保证1和n,2和n连通。
输入:第一行包含整数B,E,P,N和M,所有的整数都不超过40000,N>=3.
接下来M行,每行两个整数,表示该无向边连接的两个顶点。
输出:
一个整数,表示最小要消耗的能量。
输入样例:
4 4 5 8 8
1 4
2 3
3 4
4 7
2 5
5 6
6 8
7 8
输出样例:
22

分析:两人的路程可以看做两人先走到某一点,然后再汇合一起走.求三次单源点最短路,分别为点1出发,点2出发,点3出发,得到dis1,dis2,dis3,然后枚举汇合点i,求出dis1[i]*B+dis2[i]*e+dis3[i]*p的最小值.即为答案.因为边权为1,所以可以用bfs求最短路.

#include<cstdio>
#include<cstring>
using namespace std;
#define MAXN 100005
int fir[MAXN],edge[MAXN],nxt[MAXN],st1[MAXN],st2[MAXN],st3[MAXN],ecnt=1;
int que[MAXN],head,tail,b,e,p,n,m,ans,t1,t2,t;
bool vis[MAXN];
#define INF 2100000000
void adde(int u,int v){
    edge[ecnt]=v,nxt[ecnt]=fir[u],fir[u]=ecnt,ecnt++;
    edge[ecnt]=u,nxt[ecnt]=fir[v],fir[v]=ecnt,ecnt++;
}
void bfs(int s,int *step){
    memset(vis,0,sizeof vis);
    head=tail=0;
    que[tail++]=s;
	vis[s]=1;
    step[s]=0;
    while(head<tail){
        t=que[head++];
        for(int i=fir[t];i;i=nxt[i]){
            int v=edge[i];
            if(vis[v]==0){
                vis[v]=1;
                step[v]=step[t]+1;
                que[tail++]=v;
            }
        }
    }
}
int main()
{
    scanf("%d%d%d%d%d",&b,&e,&p,&n,&m);
    for(int i=1;i<=m;i++) {
        scanf("%d%d",&t1,&t2);
        adde(t1,t2);
    }
    bfs(1,st1);
    bfs(2,st2);
    bfs(n,st3);
    ans=INF;
    for(int i=1;i<=n;i++){
        int tmp=st1[i]*b+st2[i]*e+st3[i]*p;
        if(tmp<ans)ans=tmp;
    }
    printf("%d\n",ans);
    return 0;
}

第四题:奶牛飞盘(guard)
有n(2<=n<=20)头奶牛在玩飞盘,可是飞盘飞到了高处。现在他们要想办法叠在一起,去取飞盘。飞盘的高度为H(1 <= H <= 1,000,000,000)。给出每头奶牛的重量、高度、强壮度(能够承受的重量),问他们是否够得到,如果能够取到,求它们额外还能承受最多多少重量。(要保证每头奶牛所承受的重量都不超过它的强壮度)。
输入格式:
第一行包含N和H。
接下来N行,每行三个数,分别表示它的高度、重量和强壮度。所有的数都为正整数。
输出格式:
如果奶牛的队伍可以够到飞盘,输出还能承受的最大额外重量;否则输出“Mark is too tall”.
输入样例:
4 10
9 4 1
3 3 5
5 5 10
4 4 5
输出样例:
2

因为奶牛最多20,可以枚举奶牛的所有集合,一共2^20个.对于一个确定的奶牛集合,我们排列的顺序是根据强壮度+体重排序,强壮度+体重较大的再最下面,强壮度+体重较小的排上面,这样叠成的奶牛是最优的.当然排序可以只排一次,即一开始对所有奶牛排序.对于每个集合的奶牛,都可以利用这个顺序.
为何这么排序是最优的?留给大家自己思考了.

#include<cstdio>
#include<algorithm>
#include<cstdlib>
using namespace std;
#define MAXN 22
#define INF 0x7fffffff
#define LL long long int
int H,n;
struct Cow
{
    int w,h,s;
    bool operator <(const Cow t)const{return w+s<t.w+t.s;}
}cow[MAXN];
LL sumh,sumw,maxh,maxw,maxs=INF,anss=-1;
void dfs(int k){
    if(k>n){
       if(sumh>=H)
            if(maxs>anss)
                anss=maxs;
        return;
    }
    if(sumw<=cow[k].s){
    int lasts=maxs;
    if(cow[k].s-sumw<maxs)maxs=cow[k].s-sumw;
    sumw+=cow[k].w;sumh+=cow[k].h;
    dfs(k+1);
    sumh-=cow[k].h;sumw-=cow[k].w;maxs=lasts;
    }
    dfs(k+1);
}
int main()
{
    scanf("%d%d",&n,&H);
    for(int i=1;i<=n;i++)
        scanf("%d%d%d",&cow[i].h,&cow[i].w,&cow[i].s);
    sort(cow+1,cow+n+1);
    dfs(1);
    if(anss!=-1)printf("%I64d\n",anss);
    else printf("Mark is too tall\n");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/hefenghhhh/article/details/85016374