笔试——编程算法题

 1. 2016 "一战通offer"互联网实习季编程挑战 编程题4 串珠子

题目链接

现在A和B在玩一个游戏,这个游戏首先给了他们很多珠子,珠子有两种颜色,一种蓝色,一种黄色,我们假定两种珠子都有无限多。A需要选择n颗珠子(n为奇数),然后由B串成一串项链(顺序由B确定,这里的项链也就是一个环)。假如在最后串成的项链中,A能够找到两个不同位置的蓝色珠子,并在这两处把这个项链断开成两段,其中一段恰好长度为(n+1)/2那么A就胜利了,注意这里为整数截断除法且这个长度是不包括选出的两颗珠子的。现在请你计算出A至少要选择多少颗蓝色珠子,才能保证无论B怎么串,他都能获胜。举个例子,当A选了7颗珠子,其中有3颗蓝珠子,那么如果B串的项链为"蓝蓝红红红红蓝",则A能获胜,若B串的项链为"蓝蓝红红蓝红红",则A不能获胜。

输入描述:

给定一个整数n,为A要选出的珠子颗数.

输出描述:

请返回A至少要选的蓝珠子颗数。
输入例子:
7
输出例子:
4

这题是道公式推理题,有人这样做也能过
 if(n%3==0)
      n=(n-1)/2;
 else n=(n+1)/2;
 return n;

苦思许久,未能想出为啥这样做一定正确。 于是只有用自己的土办法来推。

问题等价于: 给出珠子总数n,求最大的蓝珠子数x且满足无论用什么办法排列都无法在移除两个蓝珠子后剩下某段长度为(n+1)/2。

最后的答案,

A至少要选的蓝珠子颗数 = x+1

定理:

在合法状态下:( 也就是移除任意两个蓝珠子,都无法得到(n+1)/2的段 )

1.设k = (n-3)/2,如果在i位置为蓝珠子,则(i+k)%n和(i-k+n)%n的位置必须为黄珠子

证明:

假设i位置为蓝珠子,且(i+k)%n的位置也为蓝珠子,则在这两个珠子之间(短的那段)共有k+1个珠子,则将这段移除后剩下来的一段长度为:n-(k+1) = n - (n-1)/2=(n+1)/2

同理(i-k+n)%n的情况。定理1即证。

然后可以知道所有相差k的,必然组成一个大环。(i,i+k,i+2k,i+3k .... (i==(i+xk)%n) ) 只需要分别处理几个环即可。对于任意一个环,长度为L,最多可以放的蓝珠子数很容易知道,为L/2(只要在这个环蓝黄珠子相间的穿)。 那么这题就可以这样做了。

class Chain {
public:
    int findK(int n) {
        // write code here
        int ret = 0;
        int key = (n-3)/2;
        int mark[100100];
        int i = 0;
        memset(mark,0,sizeof(mark));
        while(1)
        {
            int flag=0;
            for(int j=0;j<n;j++)
            {
                if(mark[j] == 0)
                {
                    flag = 1;
                    i = j;
                    break;
                }
            }
            if(flag == 0) break;
            int cnt=0;
            while(mark[i] == 0)
            {
                cnt++;
                mark[i] = 1;
                i += key;
                i %= n;
            }
            ret += cnt/2;
        }
        return ret+1;
    }
};

然后在分析最开始给出公式解法。

有一种假设,对于n为奇数,且不被3整除,则(n-3)/2与n的GCD 为1

虽然我还没证明,但是我验证了10^8的数发现都是正确的。

对于这种情况,只能找到一个这样的环,所以结果就为 (n+1)/2

而对于n为奇数,能被3整除,(n-3)/2与n的GCD不为1,所以知道可以找到两个这种环,(我猜测只能有两个环)所以结果为(n-1)/2

2. 快速排序——代码以及平均复杂度分析

void quick_sort(int *g,int l,int r)
{
    if(l >= r) return ;
    int mid = rand()%(r-l+1)+l;
    swap(g[l],g[mid]);
    int pl=l,pr=r;
    while(pl<pr)
    {
        while(pl<pr && g[pr]>g[l]) pr--;
        while(pl<pr && g[pl]<=g[l]) pl++;
        swap(g[pl],g[pr]);
    }
    swap(g[l],g[pl]);//这时只知道g[pl]<=g[l]
    quick_sort(g,l,pl-1);
    quick_sort(g,pl+1,r);
}

关于平均复杂度,找了半天资料发现《算法导论》使用概率的证明方法实在是简洁明了,瞬间就懂了。

已知待排序的数组为a[1,...,n]

假设已经将a排好序得到g[1,...,n]

设pi,j 为g[i]与g[j]需要进行比较的概率。

则总的期望比较次数(平均复杂度)为Σ(i=1...n)Σ(j=i+1...n)pi,j 

而pi,j = 2/(j-i+1)  (只有当选择g[i]或g[j]为基准点时,g[i]和g[j]才会进行比较.而选到g[k](i<k<j)时,g[i]与g[j]不会进行比较。且选到其它点时不影响)

然后根据调和级数加和为log(n),最后可以得到平均复杂度为O( nlog(n) ) 

原文地址:https://www.cnblogs.com/chenhuan001/p/5420368.html