2017.3.27考试

总述:

这场考试只考了一半就走了,当时我还没做完第二题,后面是晚自习回来继续做的。

第一题看着万分地熟悉,就感觉肯定是个Dp,因为以前好像是跟着XXX大学长学姐们听过解题思路的,但是我想不起来了,然后花了大概20分钟想dp,我推出来一个表达式:若w[i]+st[i]<w[j]+st[j],那么i放在下面,j放在上面,然后我排序了,之后就Dfs觉得没什么问题,最后很惊喜地A了特开心。

第二题是区间内的查询问题,第一反应就是线段树,然后就需要去处理删除一个点后的情况。写题的时候其实有些混乱,思路还不是特别地清晰,起码没有现在清晰,然后我就把思路写在了草稿纸上,嗯我认为这是个好办法,它对我特别有用,这样错误的思路会马上被发现,因为白纸黑字会推不通,正确的思路也可以让自己更清楚下一步要干什么,怎么样还可不可以优化一点,最不济也不会绕来绕去把自己绕进去,起码我常常干这种事(昨天物理考试因为算出来答案带根号我就没敢往上写,结果事实证明那就是正确答案,我真是太天真太善良了)最后写这题其实还是蛮多细节需要注意的,自己逻辑真的不太强,典型的想到什么是什么。

第三题我确实没有想出来,还是后来问了同学才恍然大悟的。个人觉得这对我来说是一个盲点,我想题的过程中基本上不会考虑二分这种做法,除非是那种特别直白傻缺特别明摆着的二分的题,虽然二分也写了一些题,但是经常会由于某种原因而wa二分(特别是要分后半截区间的时候,那种大于等于,严格小于什么的总是出问题),所以其实并不是特别敢写二分,这算是一个缺漏的知识点,正排时间打算把它补上。

题目:

问题 A(3039): 奶牛飞盘

时间限制: 1 Sec  内存限制: 128 MB

题目描述

有n(2<=n<=20)头奶牛在玩飞盘,可是飞盘飞到了高处。现在他们要想办法叠在一起,去取飞盘。飞盘的高度为H(1 <= H <= 1,000,000,000)。给出每头奶牛的重量、高度、强壮度(能够承受的重量),问他们是否够得到,如果能够取到,求它们额外还能承受最多多少重量。(要保证每头奶牛所承受的重量都不超过它的强壮度)。

输入

输入格式:

第一行包含N和H。

接下来N行,每行三个数,分别表示它的高度、重量和强壮度。所有的数都为正整数。

输出

输出格式:

如果奶牛的队伍可以够到飞盘,输出还能承受的最大额外重量;否则输出“Mark is too tall”.

样例输入

4 109 4 13 3 55 5 104 4 5

样例输出

2

很容易会想到要把重的放在下面或者把称重量大的放在下面,但是单一的排序很容易就会找出反例,不如来作这样一个假设,将st和w相加,再排序看一看。

对于第i和第i+1头牛,有两种放法:

i在上时:

i:st[i]-w

i+1:st[i+1]-w[i]-w

i在下时:

i:st[i]-w[i+1]-w

i+1:st[i+1]-w

对i和i+1进行假设,并尝试调换位置后可得表达式w[i]+st[i]>w[i+1]+st[i+1]时,i在下

然后搜索

#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<algorithm>
using namespace std;
const int Max=20;
int N,H,Ans;
bool flag;
struct node{
	int h,w,st,wt;
	bool operator <(const node&X)const{
		return wt>X.wt;
	}
}Cow[Max+5];
bool vis[Max+5];
int min(int a,int b){return a<b?a:b;}
void getint(int &num){
    char c;int flag=1;num=0;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
    while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
    num*=flag;
}
void Dfs(int i,int h,int lft){
	if(h>=H&&lft>=0){
		flag=1;
		Ans=max(Ans,lft);
		return ;
	}
	if(lft<=0||i==N)return ;
	for(int j=i+1; j<=N; ++j)
		Dfs(j,h+Cow[j].h,min(lft-Cow[j].w,Cow[j].st));
}
int main(){
	//freopen("guard.in","r",stdin);
	//freopen("guard.out","w",stdout);
	getint(N),getint(H);
	for(int i=1; i<=N; ++i){
		getint(Cow[i].h),getint(Cow[i].w),getint(Cow[i].st);
		Cow[i].wt=Cow[i].w+Cow[i].st;
	}
	sort(Cow+1,Cow+1+N);
	for(int i=1; i<=N; ++i)
		Dfs(i,Cow[i].h,Cow[i].st);
	if(flag)
		printf("%d\n",Ans);
	else puts("Mark is too tall");
	return 0;
}

问题 B(3040): 马拉松

时间限制: 1 Sec  内存限制: 128 MB

题目描述

奶牛贝西给他的小伙伴设计了一条马拉松比赛的线路。这条线路上一共有n(n<=100000)个点,它们分布在一个平面坐标系中。它的小伙伴毅力不够,所以不能跑完全程,于是贝西给他们安排了一条子线路(它是一段连续的点,比如设定子线路为【i,j】,表示奶牛需要从点i开始,经过点i+1,点i+2,……,最后到点j)。即使这样,奶牛们仍然可能跳过中间某个点以节省路程,当然这个点不能是子线路的起点或终点。现在,有Q(Q<100000)个操作:操作分为两种,第一种为”U I X Y”,表示将点I的位置设在坐标(X,Y)处;第二种为Q I J,表示设定子线路为点i到点j,需要查询奶牛们从i跑到j的最短路程。(路程是以曼哈顿距离计算的)他们可以跳过中间某个点。

所有的坐标值x,y均在[-1000,+1000]区间。

输入

输入:

第一行包含N和Q。接下来N行,每行两个数(X,Y),表示每个点的坐标,按照编号从小到大的顺序给出。

接下来Q行,每行表示一个操作。操作如上所述。

输出

输出:对于每一次查询,输出一个整数,表示该子线路的最短路程(曼哈顿距离)

样例输入

5 5
-4 4
-5 -3
-1 5
-3 4
0 5
Q 1 5
U 4 0 1
U 4 -1 1
Q 2 4
Q 1 4

样例输出

11
8
8

线段树去保存每个区间里删除后变小得最多的那个点引起变化的delta值,合并的时候只需考虑删除mid会不会更优的情况,更改的时候注意它的左右父亲(因为线段的单位是区间)都要更改,然后正常地查询就可以了,小心爆int。

#include<iostream>
#include<cstdio>
#include<climits>
#include<cstring>
using namespace std;
typedef long long LL;
const int Max=100000;
struct node{
	int x,y;
}P[Max+5];
struct trr{
	int l,r,d,delta;
}Tr[Max*4];
int N,Q;
//int code[Max+5];
int abs(int a){return a<0?-a:a;}
int min(int a,int b){return a<b?a:b;}
int max(int a,int b){return a<b?b:a;}
void getint(int &num){
    char c;int flag=1;num=0;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
    while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
    num*=flag;
}
int get_dis(int a,int b){return abs(P[a].x-P[b].x)+abs(P[a].y-P[b].y);}
void build(int i,int l,int r){
	Tr[i].l=l,Tr[i].r=r;
	if(l+1==r){
		Tr[i].d=get_dis(l,r);
		Tr[i].delta=INT_MAX;
		//code[l]=i;
		return ;
	}
	int mid=(l+r)>>1;
	build(i<<1,l,mid);
	build(i<<1|1,mid,r);
	Tr[i].d=Tr[i<<1].d+Tr[i<<1|1].d;
	Tr[i].delta=min(Tr[i<<1].delta,min(Tr[i<<1|1].delta,get_dis(mid-1,mid+1)-get_dis(mid-1,mid)-get_dis(mid,mid+1)));
}
void change(int i,int s){
	if(s<Tr[i].l||s>Tr[i].r)	return ;
	if(Tr[i].l+1==Tr[i].r){
		Tr[i].d=get_dis(Tr[i].l,Tr[i].r);
		return ;
	}
	int mid=(Tr[i].l+Tr[i].r)>>1;
	change(i<<1,s);
	change(i<<1|1,s);
	Tr[i].d=Tr[i<<1].d+Tr[i<<1|1].d;
	Tr[i].delta=min(Tr[i<<1].delta,min(Tr[i<<1|1].delta,get_dis(mid-1,mid+1)-get_dis(mid-1,mid)-get_dis(mid,mid+1)));
}
LL get_ans(int i,int l,int r,LL &delta){
	if(Tr[i].l>=r||Tr[i].r<=l) return 0;
    if(l<=Tr[i].l&&Tr[i].r<=r){
        delta=min(delta,Tr[i].delta);
        return Tr[i].d;
    }
    int mid=(Tr[i].l+Tr[i].r)/2;
    if(l<mid&&mid<r) delta=min(delta,get_dis(mid-1,mid+1)-get_dis(mid-1,mid)-get_dis(mid,mid+1));
    return get_ans(i<<1,l,r,delta)+get_ans(i<<1|1,l,r,delta);
}
int main(){
	getint(N),getint(Q);
	for(int i=1; i<=N; ++i)
		getint(P[i].x),getint(P[i].y);
	build(1,1,N);
	char ord[3];
	int s,t,x,y;
	for(int i=1; i<=Q; ++i){
		scanf("%s",ord);
		if(ord[0]=='Q'){
			getint(s),getint(t);
			LL delta=0;
			LL Ans=get_ans(1,s,t,delta);
			if(delta<0)	Ans+=delta;
			printf("%lld\n",Ans);
		}
		else {
			getint(s),getint(x),getint(y);
			P[s].x=x,P[s].y=y;
			change(1,s);
		}
	}
	return 0;
}

问题 C(3041): 奶牛慢跑

时间限制: 1 Sec  内存限制: 128 MB

题目描述

有n(n<=100000)头奶牛在一个无穷长的小道上慢跑。每头奶牛的起点不同,速度也不同。小道可以被分成多条跑到。奶牛只能在属于自己的跑道上慢跑,不允许更换跑道,也不允许改变速度。如果要慢跑t(t<=1000000000)分钟,要保证在任何时候不会有同一跑道上的奶牛相遇,请问最少需要多少条跑道。奶牛开始在哪条跑道是可以随意设置的。

输入

输入格式:第一行两个整数n,t。

接下来的n行,每行包含两个整数,表示奶牛的位置和速度。位置是非负整数,速度是正整数。所有的奶牛的起点都不相同,按起点递增的顺序给出。

输出

输出格式:

最少的跑道数。

样例输入

5 30 11 22 33 26 1

样例输出

3


我们可以算出每头奶牛的起点和终点,模拟有N条跑道,看哪些奶牛可以尽可能地放进之前的跑道里,二分去找一个最小的大于当前的终点值的跑道,将其放进去。

#include<iostream>
#include<cstdio>
#include<climits>
using namespace std;
typedef long long LL;
const int Max=100000;
struct node{
	int p,v;
	LL end;
}Cow[Max+5];
int N,T,Ans=1;
LL E[Max+5];//第i条跑道的最靠后一人的结束位置
void getint(int &num){
    char c;int flag=1;num=0;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
    while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
    num*=flag;
}
int upbound(LL val){//找小于等于当前结束位置的最大位置的后一个
	int l=1,r=Ans,mid;
	while(l<=r){
		mid=(l+r)>>1;
		if(val==E[mid])	return	mid+1;
		else if(val<E[mid])	l=mid+1;
		else r=mid-1;
	}
	return l;
}
int main(){
	getint(N),getint(T);
	for(int i=1; i<=N; ++i){
		getint(Cow[i].p),getint(Cow[i].v);
		Cow[i].end=Cow[i].p+1LL*Cow[i].v*T;
		E[i]=LONG_LONG_MAX;
	}
	E[1]=Cow[1].end;
	for(int i=2; i<=N; ++i){
		int pos=upbound(Cow[i].end);
		E[pos]=Cow[i].end,Ans=max(Ans,pos);
	}
	printf("%d\n",Ans);
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值