五道算法题

来源:互联网 发布:五笔打字软件 编辑:程序博客网 时间:2024/05/21 07:10

 

雅虎:

1.对于一个整数矩阵,存在一种运算,对矩阵中任意元素加一时,需要其相邻(上下左右)某一个元素也加一,现给出一正数矩阵,判断其是否能够由一个全零矩阵经过上述运算得到。

 

2.一个整数数组,长度为n,将其分为m份,使各份的和相等,求m的最大值

  比如{3,2,4,3,6} 可以分成{3,2,4,3,6} m=1;  

  {3,6}{2,4,3} m=2

  {3,3}{2,4}{6} m=3 所以m的最大值为3

解:poj 1011,搜索+强剪枝

Description

George took sticks of the same length and cut them randomly until all parts became at most 50 units long. Now he wants to return sticks to the original state, but he forgot how many sticks he had originally and how long they were originally. Please help him and design a program which computes the smallest possible original length of those sticks. All lengths expressed in units are integers greater than zero. 
 Output

The output should contains the smallest possible length of original sticks, one per line. 

首先说一下这个题的解题思想。

1.选取某一个开始长度,开始组合小木棒,这个开始长度的限制条件为 不小于木棒最大长度,不大于所有木棒长度和,能被长度和整除
2.从可用的最长的那根小木棒开始组合木棒,找出所有的结果集,找到结果集后开始组合下一根木棒。
3.直到所有的小木棒都被组合完成,搜索结束。
http://blog.csdn.net/night_blue/article/details/2966056

思路二:

1.len>=max{a[i]} && len|sum(a[i])
2.为了避免重复搜索,令每个大S的组成中,小S的长度依次递减,这样就需要在搜索之前对a[i]排序;全部的大S的第一段小S依次递减
3.如果在某层搜索中,尝试将a[j]加入到第i个大S的组成中,如果最终a[j]没有被使用,且a[j+1]==a[j],不需要继续尝试a[j+1]
4.如果此次是在尝试第i个大S的第一段小S a[j],a[j]为当前可以被使用的最长的小S,如果此次尝试失败,直接退出搜索,即退回到对第i-1个大S的搜索。试想:失败说明现在使用a[j]是不可行的,那么什么时候使用a[j]呢?如果没有退出搜索,肯定会在之后的搜索中使用a[j],因为所有的小S必须都使用。之后的a[j]和最初尝试的a[j]有什么不同呢?没有不同,它们等价,因此之后也不会成功,不需要继续搜索。

都一致:先对这些数进行排序;

  1. #include <iostream>  
  2. #include <algorithm>  
  3. using namespace std;   
  4.   
  5. int sticks[64], n, len, num;   (num为可以得到多少对)
  6. bool used[64];   
  7.   
  8. bool compare(int a, int b)   
  9. {   
  10.     return a > b;       
  11. }   
  12.   
  13. bool dfs(int cur, int left, int level)    
  14. {   //cur: 当前已经计算的木棒编号,left:该段还剩的长度,level:已经成功的木棒数  
  15.     if(left == 0) {//匹配一根木棒成功  
  16.         if(level == num-2)   
  17.             return true;   
  18.         for(cur = 0; used[cur]; cur++)   //找到第一个还没被用的
  19.             ;   
  20.         used[cur] = true;   
  21.         if(dfs(cur+1, len-sticks[cur], level+1))   
  22.             return true;   
  23.         used[cur] = false;   
  24.         return false;   
  25.     } else {   
  26.         if(cur >= n-1)   //最后一根了
  27.             return false;   
  28.         for(int i = cur; i < n; i++) {   
  29.             if(used[i])   
  30.                 continue;   
  31.             if((sticks[i] == sticks[i-1]) && !used[i-1])   //
  32.                 continue;      
  33.             if(sticks[i] > left)   
  34.                 continue;   
  35.             used[i] = true;   
  36.             if(dfs(i, left-sticks[i], level))   
  37.                 return true;   
  38.             used[i] = false;    
      1. //不行的话,就不会使用这个
  39.         }      
  40.         return false;   
  41.     }   
  42. }   
  43.   
  44. int main()   
  45. {   
  46.     while(cin>>n) {   
  47.         if(n == 0)   
  48.             break;   
  49.         int sum = 0;   
  50.         for(int i = 0; i < n; i++) {   
  51.             scanf("%d", &sticks[i]);   
  52.             sum += sticks[i];   
  53.         }   
  54.         sort(sticks, sticks+n, compare); //由大到小排序     
  55.         bool end = false;   
  56.         for(len = sticks[0]; len <= sum/2; len++) {   
  57.             if(sum%len == 0) {   
  58.                 used[0] = true;   
  59.                 num = sum/len;   
  60.                 if(dfs(0, len-sticks[0], 0)) {   
  61.                     end = true;   
  62.                     printf("%d\n", len);   
  63.                     break;     
  64.                 }      
  65.                 used[0] = false;   
  66.             }      
  67.         }   
  68.         if(!end)   
  69.             printf("%d\n", sum);   
  70.         memset(used, 0, sizeof(used));   
  71.     }   
  72.     //system("pause");  
  73.     return 0;   
  74. }  


搜狐:

 

3.四对括号可以有多少种匹配排列方式?比如两对括号可以有两种:()()和(())

卡特兰数(Catalan)

例子1:Cn= n对括号正确匹配组成的字符串数,例如 3对括号能够组成:((())) ()(()) ()()() (())() (()())

例子2:Cn= n+1个数相乘,所有的括号方案数。例如, 4个数相乘的括号方案为:((ab)c)d (a(bc))d (ab)(cd) a((bc)d) a(b(cd))

例子3:Cn= 拥有 n+1 个叶子节点的二叉树的数量。例如 4个叶子节点的所有二叉树形态:

分析:“卡特兰数”除了可以使用公式计算Cn=C(n,2n)-C(n-1,2n),也可以采用“分级排列法”来求解。以 n对括弧的合法匹配为例,对于一个序列 (()而言,有两个左括弧,和一个右括弧,可以看成“抵消了一对括弧,还剩下一个左括弧等待抵消”,那么说明还可以在末尾增加一个右括弧,或者一个左括弧,没有左括弧剩余的时候,不能添加右括弧。 

arr = new double[n + 1];//arr代表有 k个括弧的时候,剩余 "("个数为 i的排列方案个数 arr[1] = 1;

        double Catalan(int n)
        {
            if (n == 0) return 1; 
    for (int i = 2; i <= 2 * n; i++)
            {
                var m = i <= n ? i : 2 * n + 1 - i;
                for (int j = (i - 1) & 1; j <= m; j += 2)
                {
                    if (j > 0) arr[j - 1] += arr[j];
                    if (j < n) arr[j + 1] += arr[j];


                    arr[j] = 0;
                }
            }
            return arr[0];
        }

 

创新工场:

 

4.求一个数组的最长递减子序列 比如{9,4,3,2,5,4,3,2}的最长递减子序列为{9,5,4,3,2}

 

微软:

5.一个数组是由一个递减数列左移若干位形成的,比如{4,3,2,1,6,5}是由{6,5,4,3,2,1}左移两位形成的,在这种数组中查找某一个数。

 

 

 

我的想法:

1.

说法一:

网上有人提出两个弱判断条件如下:(原题的必要条件,非充要
1) 将矩阵分成黑白棋盘格, 所有黑色格子的数字和等于白色格子的.
2) 对任意一个位置, 他的值不大于周围(上下左右)4个临格的数值的和.

 

 

2. poj 1011,搜索+强剪枝

 

3. Catalon?没验证,但是DP可以:

dp[i][j]表示从i->j位置的这些括号的最大组成种数,

dp[i][j] = dp[i-1][j-1] //i是(,j是),并且他们搭配

              + sigama(dp[i][k] * dp[k+1][j]),  i与k搭配,k+1与j搭配,   i<k<j枚举

初始条件:dp[i][i+1] = 1 (长度为2只能是"()"),

dp[i][i] = 0 (长度为1不能搭配)

 

4.DP求LCS问题的变形

肯定用动态规划来做,想了一下:前n个元素组成的子数组中的最长递减子序列和前n+1个元素组成的子数组中最长递减子序列没有太大的关系;

需要把问题转化一下,再由 大问题 转化为 小问题;

 

可以发现以 数组第n+1个元素为结尾的最长递减子序列和以第1,2,3,。。。n个元素为结尾的最长递减子序列长度有关系;

而此序列的最长递减子序列的长度肯定是 以数组第1,2,3,4....n为结尾的最长递减子序列中的最大值,so记录一下最大值就可以了;

 

首先看该问题的子问题,对于第i(i>0 i<N)个元素而言,以其结尾的递增子序列长度由前i-1个数组成的所有递增子序列长度来决定。于是该问题就分为了i-1个子问题

maxLenIncr[i] = max{ maxLenIncr[k]+1, 1} if (a[i]>a[k] && k>=0&&k<i && i>0)

maxLenIncr[0]=1;

maxLenIncr[i] 表示以i结尾的最长递增子序列长度。

代码如下:

/*maxLenIncr[i] 表示以i结尾的最大递增子串长度*/

maxLenIncr[i] = max{ maxLenIncr[k]+1 if a[i]>a[k] } ( k>=0&&k<I && i>0);

int maxLenIncr(int* p, int len)

{

      int* maxLenIncr=new int[len];

      int maxLen=1;

 

      /*Default value=1*/

      for(int i=0; i<len; i++)

      {

            maxLenIncr[i] = 1;

      }

      for(int i=1; i<len; i++)

      {

            for(int k=0; k<i; k++)

            {

                  if(a[i] > a[k])

                  {

                        maxLenIncr[i]=max(maxLenIncr[k]+1, 1);

                        if(maxLenIncr[i]>maxLen)

                              maxLen=maxLenIncr[i];

                  }

            }

      }

      return maxLen;

}

 

 

 

5. 一种分情况的二分:

首先观察这个序列,先下降,然后突然上升(暂且叫断点吧),接着又下降,而且有个性质

(*)断点前面的所有数都比断点后面的所有数小

令当前二分的区间是[l,h],那么这个区间有3中可能性:

(1)落在断点前的那个下降区间里面;

(2)落在断点后的那个下降区间里面;

(3)跨区间。

我们看怎么判断当前落在哪个区间里,另a是原来的数组

    如果a[l] > a[h],由性质(*)得到不可能是跨区间的(否则因为l在断点前的区间,h在断点后的区间,那么肯定有a[l]<a[h]),那么只可能是(1)或者(2),这样这个区间就是普通的下降区间,用常规的二分在这个区间里找数。

    如果a[l] < a[h], 那么肯定是跨区间的,考虑中间值a[(l+h)/2],如果这个中间值>a[l],那么根据性质(*)中间值在断点后,否则就在断点之前。如果中间值是断点之后,那么[(l+h)/2, h]形成一个下降区间,看我们找的value是不是落在这里面,如果是下一部搜索区间就是[(l+h)/2, h],不然就是[l, (l+h)/2]。如果中间值是断点之前,那么[l, (l+h)/2]形成一个下降区间,value如果落在里面,下一步搜索区间就是[l, (l+h)/2],不然就是[(l+h)/2, h]。

原创粉丝点击