一道百度之星编程比赛的题目
题目描述:一个正整数有可能可以被表示为n(n>=2)个连续正整数之和,如:
15=1+2+3+4+5
15=4+5+6
15=7+8
请编写程序,根据输入的任何一个正整数,找出符合这种要求的所有连续正整数序列。
输入数据:一个正整数,以命令行参数的形式提供给程序。
输出数据:在标准输出上打印出符合题目描述的全部正整数序列,每行一个序列,每个序列都从该序列的最小正整数开始、以从小到大的顺序打印。如果结果有多个序列,按各序列的最小正整数的大小从小到大打印各序列。此外,序列不允许重复,序列内的整数用一个空格分隔。如果没有符合要求的序列,输出“NONE”。
例如,对于15,其输出结果是:
1 2 3 4 5
4 5 6
7 8
对于16,其输出结果是:
NONE
解法收集:
#i nclude
#i nclude
main()
{
long liDate; /*输入的正整数*/
long iCount;/*项数*/
long i,min,max;
int flag=0;
system("cls");
printf("Please inout the number:/n",&liDate);/*输入正整数*/
scanf("%ld",&liDate);
for(iCount=(long)sqrt(2*liDate);iCount>=2;iCount--)
if(2*liDate%iCount==0)
if(iCount%2) /*项数为奇数*/
{
flag=1;
min=liDate/iCount-(iCount-1)/2;
max=liDate/iCount+(iCount-1)/2;
for(i=min;i<=max;i++)
printf("%ld ",i);
printf("/n/n");
}
else if(2*liDate/iCount%2) /*项数为偶数*/
{
flag=1;
min=liDate/iCount-iCount/2+1;
max=liDate/iCount+iCount/2;
for(i=min;i<=max;i++)
printf("%ld ",i);
printf("/n/n");
}
printf("/n");
if(flag==0) printf("NONE/n");
system("pause");
}
n个连续整数的最小值为:
1+2+3+4+…+n=(n+1)*n/2,即liDate可以表示的项数最多的形式不超过1+2+3+4+…+n的项数。
而n*(n+1)=2*liDate,所以定有n<=sqrt(2*liDate)
若正整数可以表示为:liDate=a1+a2+a3+…an
则一定可以表示为:liDate=(a1+an)*n/2
则项数n=2*liDate/(a1+an)
因a1+an为整数,故2*liDate%n一定等于0,故有程序中的条件if(2*liDate%iCount==0)
若n为奇数,一定可以表示为a1+a2+…an的形式,故有程序中的条件if(iCount%2)。
若n为偶数,则a1+an一定为奇数(因为若a1为奇数,则an为偶数;若a1为偶数,则an为奇数,总之,其和定为奇数),即(a1+an)%2==1,即2*liDate/n%2==1,故有程序中的条件:else if(2*liDate/iCount%2)
不使用第三个变量交换整数a与整数b
解:
基本思想:
a=a+b
b=a-b
a=a-b
算法改进:
a=a^b
b=b^a
a=a^b
合并:a^=b^=a^=b (经典!!!)
扩展:
#i nclude
void main()
{
int a = 3;
int b = 2;
a += b += 4;
printf("a=%d, b=%d/n",a,b);
}
output: a=9, b=6
一个整数(32bit),按位逆序。提示,按位运算,效率高。
解:
基本思想:分治
x = (x & 0x0000FFFF) << 16 | (x & 0xFFFF0000) >> 16;
x = (x & 0x00FF00FF) << 8 | (x & 0xFF00FF00) >> 8;
x = (x & 0x0F0F0F0F) << 4 | (x & 0xF0F0F0F0) >> 4;
x = (x & 0x33333333) << 2 | (x & 0xCCCCCCCC) >> 2;
x = (x & 0x55555555) << 1 | (x & 0xAAAAAAAA) >> 1;
说明:逆着写也可以。
百度2005年A*star比赛初赛第二题:重叠区间大小
题目描述:请编写程序,找出下面“输入数据及格式”中所描述的输入数据文件中最大重叠
区间的大小。
对一个正整数n,如果n在数据文件中某行的两个正整数(假设为A和B)之间,即A<=n<=B或
A>=n>=B,则n属于该行;如果n同时属于行i和j,则i和j有重叠区间;重叠区间的大小是同时属
于行i和j的整数个数。
例如,行(10 20)和(12 25)的重叠区间为[12 20],其大小为9;行(20 10)和(12
18)的重叠区间为[10 12],其大小为3;行(20 10)和(20 30)的重叠区间大小为1。
输入数据:程序读入已被命名为input.txt的输入数据文本文件,该文件的行数在1到1,000,000
之间,每行有用一个空格分隔的2个正整数,这2个正整数的大小次序随机,每个数都在1和
2^32-1之间。(为便于调试,您可下载测试input.txt文件,实际运行时我们会使用不同内容的
输入文件。)
输出数据:在标准输出上打印出输入数据文件中最大重叠区间的大小,如果所有行都没有重叠
区间,则输出0。 评分标准:程序输出结果必须正确,内存使用必须不超过256MB,程序的执
行时间越快越好。
解题思路:(1)线段树的方法;(2)排序+迭代搜索。第一种方法没有研究,第二种方法网上有人
给出了思路和源码。下面是第二种方法的介绍:
很容易想到一个简单的算法。读入所有区间到一个数组中,
range[][2],行数为n,给定的输入中一个区间可能不按顺序来,读入的时候可以很容易把它弄成
有序的。然后对每一个区间range[],0i区间的重叠
区间,找出跟区间range[]重叠最大的,比较所有这种最大值,就可以找出最终最大的。这
个时间复杂度是 o(n^2). 行数很多时是个问题。而题目说行数可以到达10^6,显然不行。怎么
办呢?我的下一个想法是对range[]进行排序,我们知道有排序算法可以在o(nlogn)时间内求出
结果。按什么排序呢?把区间按起始位置进行排序感觉可能有用。然后怎么办呢? 怎么样可以
确保每个区间都比一下,而且能找到最大的重叠区间呢?可以考虑一个动态的过程,我们可以
想象一下把排序后的各个区间逐个加入的过程。开始有一个区间,然后不断地加入区间,维护
最大的重叠区间。开始有一个区间[start,end],最大重叠区间为0,然后加入一个区间
[start2,end2],因为区间是按 start从大到小排序的,所以start2>=start. 这时可以有三种
情况。1) 第一个区间包含第二个区间 2)第一个区间与第二个区间相交 3)第一个区间与第二
个区间不相交
如下图所示:
1)
start end
| |
| |
start2 end2
2)
start end
| |
| |
start2 end2
3)
start end
| |
| |
start2 end2
我们可以针对每种情况求出重叠区间,这是目前的最大重叠区间,问题是,下一步呢?
比如又加了一个区间 start3,end3
我们需要知道第三个区间与前两个区间的最大重叠区间。
我们知道 start3>=start2>=start1.
如果第一个和第二个区间属于第一种情况,则第三个区间和第一个区间的重叠区间是第三个区
间和前两个区间的重叠区间中最大的
如果属于后两种情况,则第三个区间和第二个区间的重叠区间是最大的。
更一般地,仔细观察,我们可以发现,新引入的区间和前面区间的最大重叠区间
一定在前面区间中具有最大end的区间和新考察的区间之间。我们不需要考察以前区间的start
点,
因为以前所有区间的start点一定不大于新区间的start点,如果有重叠区间的话一定不小于新
区间的start.
只需要考察新区间与以前区间的最大end,比较属于上述三种情况中的那一种就可以了。
我们可以维护一个全局的end点gend,每次加入新区间,如果新区间startNew,endNew的end点
endNew
没有大于gend(第一种情况)
则新区间和前面所有区间的最大重叠区间一定是 endNew-startNew。
如果属于第二种情况,则最大重叠区间一定是 gend - startNew, 然后更改 gend = endNew
如过属于第三种情况,则没有重叠区间。gend改为 endNew
比较这个最大重叠区间的和原先的最大重叠区间,我们就可以知道到目前为止最大的重叠区间
。
通过保持gend和到目前为止的最大重叠区间,这种方法对每个新的区间只需要考察一次。
这样就可以在线形时间内得到最后的结果。
基本思路清楚了之后,开始写代码,首先读入输入数组数据,然后排序。什么排序方法呢?
冒泡?不行,复杂度太高。插入?突然觉得很傻,没有必要啊。我可以在读数据的时候就完成
排序阿。
二叉排序树!把数据读完,二叉排序树就可以建起来了。而中序遍历就可以得到一个有序的区
间列表。
对每个新节点,需要插入一次,而插入一次平均的最坏的复杂度也就是logn,所以建树的整个
复杂度为
o(nlogn),而找重叠区间复杂度为o(n),所以最终复杂度为o(nlogn)
代码:
#include
#include
#include
typedef struct rangeNode
{
unsigned int start;
unsigned int end;
struct rangeNode* left;
struct rangeNode* right;
}rangeNode;
rangeNode* insertANode(rangeNode* r,unsigned int start,unsigned int end)
{
rangeNode* newNode;
if(!r)
{
newNode = (rangeNode*) malloc(sizeof(rangeNode));
newNode->start = start;
newNode->end = end;
newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
if(start<=r->start) /*insert to left*/
r->left = insertANode(r->left,start,end);
else
r->right = insertANode(r->right,start,end);
return r;
}
void findMaxOverlap(rangeNode* root,unsigned int *maxOverlap,unsigned int* maxEnd)
{
if(root->left)
findMaxOverlap(root->left,maxOverlap,maxEnd);
unsigned int start = root->start;
unsigned int end = root->end;
unsigned int overlap;
if(0 == *maxEnd) /*maxEnd has not been assigned before */
{
*maxEnd = root->end;
}
else if(start >*maxEnd) /*no overlap */
{
*maxEnd = end;
}
else /*overlap, two cases*/
{
if(*maxEnd>end) /*include */
{
overlap = end - start + 1;
}
else /*intersect*/
{
overlap = *maxEnd - start + 1;
*maxEnd = end;
}
if(overlap > *maxOverlap)
*maxOverlap = overlap;
}
if(root->right)
findMaxOverlap(root->right,maxOverlap,maxEnd);
}
rangeNode* readInput()
{
FILE* fp;
fp = fopen("input.txt","r");
if(!fp)
{
fprintf(stderr,"cannot read input file input.txt/n");
return NULL;
}
char buf[128];
char* p = buf;
unsigned int first,second;
rangeNode* root = NULL;
while(NULL != fgets(buf,sizeof(buf),fp))
{
first = atol(buf);
if(0 == first)
{
break;
}
p = strchr(buf,' ');
p++;
second = atol(p);
if(first<=second)
{
root = insertANode(root,first,second);
}
else
{
root = insertANode(root,second,first);
}
}
fclose(fp);
return root;
}
void clean(rangeNode* root)
{
if(root->left)
clean(root->left);
if(root->right)
clean(root->right);
free(root);
}
int main(int argc,char** argv)
{
rangeNode* root = readInput();
if(!root)
{
printf("0/n");
return -1;
}
unsigned int maxOverlap = 0;
unsigned int maxEnd = 0;
findMaxOverlap(root,&maxOverlap,&maxEnd);
printf("max overlap %u/n",maxOverlap);
clean(root);
return 0;
}
据说是Google的面试题
题:对一个数组S,求其中满足要求的最大元素C。要求C满足等式C=A*B,其中A、B也是数组S中的元素。请用能想到的最优算法,分析时间和空间复杂度。
方法1:时间O((n^2)log(n))
1.排序(nlog(n))
2.开一个新数组,长度为(n-1)n/2,存储两两相乘的结果(n^2)
3.把排序好的数组和新数组求交集,((n^2)log(n))
方法2:时间O(n^1.5)
1. 把原来的数组进行hash,得到hash Table。O(n)
2.排序,使得数组S前面小,后面大。O(nlogn)
3.从大的一端开始搜索,设为S,计算b=S/S[j],其中S[0]<=S[j]<=S^0.5。大约O(n^1.5)
4.if (hash == true) then bingo。O(1)