转贴]一些算法题目

一道百度之星编程比赛的题目


题目描述:一个正整数有可能可以被表示为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)

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值