本次上机主要考察知识点为快排、二分查找、优先队列、数组线性操作等算法知识。
关于题目难度,我感觉对于我这种做题仅限于北航oj的小菜比来说略大。上机前给出了知识点,但实际落实到题目中时,我却根本看不出题目在考察哪个知识点(或许我只能做做裸题吧),比如,上机时听说第一题是二分,却总是找不到分什么。B题考察优先队列和数组操作(其实看穿本质,用公式也能过),C题利用前缀和结合STL中的map(用数组会MLE),D题考察数组操作和优先队列(当然,我用set集合也能达到同样效果),E题不谈,听大佬说不适合我这种渣渣做,G题直接模拟题目的步骤就行(感谢助教送的100分)。
总地来说,我还是见识太少,练习不够,缺少那种短时间迅速思考求解题目的能力。因此针对以上从上机中发现的不足,我有必要勤于思考,多加练习,争取一眼看穿问题的本质,而不是拿到问题无从下手。
此题考察二分查找。
输入时将每个圈的左右顶点放入两个数组l和r,先对两个数组排序,对于每个圈,分别计算它左边不相交的圈和右边不相交的圈。对于左边,lower_bound(r,r+n,l[i])-r即为所求圈数;对于右边,先求出与它相交的圈数pos1,然后用总圈数-pos1-1即为所求圈数。最后将所有圈数相加,但要注意此时重复求了每个圈的不相交数,因此要除以2。
二分查找的时间复杂度为log(n),外面有层循环,所以算法的时间复杂度为nlog(n)。
关于lower_bound和upper_bound:这是STL提供的两个二分查找函数,lower_bound返回第一个>=给定元素的元素位置,要求元素从小到大排列;返回第一个>给定元素的元素位,要求元素从小到大排列。
关于STL中查找函数的具体描述:http://blog.csdn.net/zhongkeli/article/details/6883288
LL ans,x,R;
LL l[maxn],r[maxn];
intmain()
{
intn;
while(~scanf("%d",&n))
{
for(int i =0;i < n;i++) ~scanf("%lld%lld",&x,&R),l[i] =x-R,r[i] = x+R;
sort(l,l+n);sort(r,r+n);
ans = 0;
for(int i =0;i < n;i++)
{
int tmp = n-1,pos_1,pos_2;
pos_1 = upper_bound(l,l+n,r[i])-l;
if(pos_1&&l[pos_1-1] <= r[i]) pos_1--;
tmp -= pos_1;//当前圈右边与它分开的圈数
pos_2 = lower_bound(r,r+n,l[i])-r; //当前圈左边与它分开的圈数
tmp += pos_2;
ans += tmp;//因为起始tmp为n,所以减一
}
printf("%lld\n",ans/2);//每个答案都重复数了一次,所以要除以二
}
}
此题分两种情况考虑。
第一种,相同编号指令间的最短间隔时间很小,以至于完成任务不需要思考人生的时间。这种情况下,ans为x,即总指令个数。
若不满足上述情况,则考虑一个公式:ans = (max_x-1)*(t+1) + max_cnt其中x为总指令个数,max_x为数量最多的那些指令的指令个数,t为相同编号指令间的最短间隔时间,max_cnt为数量最多的那些指令的编号总数。
以下给出证明:
以样例为例,将总时间分为两部分max_x-1次完整的分配和最后剩下的max_cnt,即
1-->2-->思考人生-->1-->2-->思考人生-->1-->2,蓝色为第一部分,红色为第二部分,此时,max_x = 3,t = 2,max_cnt = 2,因此ans = (3-1)*(2+1)+2 = 8。
最后,综合以上两总情况,给出最后的公式:ans = max(x,(max_x-1)*(t+1) + max_cnt)。
由于推导出了公式,因此,时间复杂度为O(n)。
inta[31];
intmain()
{
intx,y ;
while(~scanf("%d",&x))
{
memset(a,0,sizeof(a));
for(int i =0;i < x;i++)
{
scanf("%d",&y);
a[y]++;//记录各指令出现的次数
}
int max_x =0,max_cnt =0;
for(int i =1;i <=30;i++)
{
if(a[i] > max_x)
{
max_cnt = 0;//出现新的最大值,种数归零
max_x = a[i];//更新最大指令数
}
if(a[i] == max_x)
{
max_cnt++;//最大编号种数加一
}
}
int t;
scanf("%d",&t);
intans = max(x,(max_x-1)*(t+1) + max_cnt);//核心公式
printf("%d\n",ans);
}
}
这题可以看作一道数组线性操作题,但是真的用数组却又会MLE,因此我选用map来避免。
此题的核心是前缀和,mp[tmp_sum]记录每行达到tmp_sum宽度时缝隙的个数。输入结束后,遍历map,找到最大缝隙数max_sum,用n-max_sum即为答案。
因为核心操作在输入时已经完成,因此时间复杂度为O(mn)。
关于一维前缀和: 这个优化主要是用来在O(1)时间内求出一个序列a中,a[i]+a[i+1]+……+a[j]的和。具体原理十分简单:用sum[i]表示(a[1]+a[2]+……+a[i]),其中sum[0]=0,则(a[i]+a[i+1]+……+a[j])即等于sum[j]-sum[i-1]。
主要应用于降维。
参考资料:http://blog.csdn.net/K_rew/article/details/50527287
map<int,int>mp;
intmain()
{
intn,m;
inttmp,tmp_sum;
while(~scanf("%d%d",&n,&m))
{
mp.clear();
for(int i =0; i < n; i++)
{
tmp_sum = 0;
for(int j =0; j < m; j++)
{
scanf("%d",&tmp);
if(j == m-1)break;//达到总宽度时的缝隙无法通过
tmp_sum += tmp;//前缀和
mp[tmp_sum]++;//此宽度的缝隙数加一
}
}
if(m ==1) printf("%d\n",n);//因为前面达到总宽度时未计算前缀和,因此m=1单独讨论
else
{
int max_sum =0;//记录同一宽度下缝隙数量的最大值
for(map<int,int>::iterator it = mp.begin(); it != mp.end(); it++)
{
max_sum =max(max_sum,(*it).second);
}
printf("%d\n",n-max_sum);//n-缝隙数即为撞的建筑物数
}
}
}
此题类似于“四和归零”,但不能照搬套路,否则会MLE。
思路是先将两数组排序,一个升序,一个降序,然后取两个指针i和j指向数组当前元素,若当前元素和小于t,i向前移动(使得元素和增大);若当前元素和大于t,j向前移动(使得元素和增大),每当元素和等于t时,将这对元素当作pair放入集合(set)S中,利用set元素的唯一性自动去重,同时利用set元素有序性自动排序,可谓一举两得,实在美滋滋。最后判断S是否为空,是则输出“OTZ”,否则按顺序将set中的元素一一打印出来。不过要记得每组数据最后要输出换行,不然会PE哦~
本算法的核心在于两指针i和j的移动,因此时间复杂度为O(n)。
LL a[maxn],b[maxn];
boolcmp(const LL &a,const LL &b)
{
returna>b;
}
intmain()
{
intn;
set
while(~scanf("%d",&n))
{
for(int i =0; i < n; i++)
{
scanf("%lld",&a[i]);
}
for(int i =0; i < n; i++)
{
scanf("%lld",&b[i]);
}
LL t;
scanf("%lld",&t);
sort(a,a+n);//升序排列
sort(b,b+n,cmp);//自定义cmp,降序排列
int i =0,j =0;
bool fla =true;
while(i < n&&j < n)
{
if(a[i] + b[j] == t)//满足条件的元素对放入set中
{
pair
p.first = a[i];
p.second = b[j];
S.insert(p);
i++;j++;
}
elseif(a[i] + b[j] < t)//小于t时i前移
{
i++;
}
Else//大于t时j前移
{
j++;
}
}
if(S.empty())
{
printf("OTZ\n");
}
else
{
for(set
{
pair
p = *it;
printf("%lld %lld\n",p.first,p.second);
}
}
while(!S.empty())//清空set
{
S.clear();
}
puts("");//记得最后输出换行
}
}
和上次上机相同,这次的真快排同样只需模拟题目所给出的步骤就行。
因为只需进行两次递归,因此时间复杂度为O(n)。
#include
#define maxn 1000007
inta[maxn];
voidmov(int &a,int &b)
{
inthold = a;
a = b;
b = hold;
}
intmain()
{
intn;
while(~scanf("%d",&n))
{
for(int i =0; i < n; i++)
{
scanf("%d",&a[i]);
}
int i =0;
int j = n-1;
int mid = n/2;
int key = a[mid];
while(i <= j)//第一遍递归
{
while(a[i] < key)
{
i++;
}
while(a[j] > key)
{
j--;
}
if(i <= j)
{
mov(a[i],a[j]);
i++;
j--;
}
}
int tem = i;
i = 0;
j = tem - 1;
mid = tem/2;
key = a[mid];
while(i <= j)//第二遍递归
{
while(a[i] < key)
{
i++;
}
while(a[j] > key)
{
j--;
}
if(i <= j)
{
mov(a[i],a[j]);
i++;
j--;
}
}
for(int k = i;k < tem;k++)//取出第二部分的元素
{
printf("%d ",a[k]);
}
puts("");
}
}