挑战程序设计竞赛 —— 蓄势待发(准备篇)

一.蓄势待发——准备篇

       1.编写一个程序,判断纸片上缩写的数字是k1,k2, ……,kn时,是否存在抽取4次和为m的方案,如果存在输出Yes,否则输出No     

       输入:

              n= 3

              m= 10

              k= { 1, 3, 5}      

       输出:

              Yes (例如4次抽取的结果是1,1,3,5时,和为10。)

      

       采取的方法可以遍历所有的可能情况。

#include <iostream>
 
using namespace std;
 
const int MAX_N = 3;
 
int main()
{
    int n = 3;
    int m = 10;
    int k[MAX_N] = { 1, 3, 5};
 
    bool f = false;
 
    for( int a = 0; a < n;a++)
    {
        for(int b = 0; b <n; b++)
        {
            for(int c = 0; c< n; c++)
            {
                for(int d = 0;d < n; d++)
                {
                    if( k[a] +k[b] + k[c] + k[d] == m)
                    {
                        f =true;
                    }
                }
            }
        }
    }
 
    if( f)
    {
        cout <<"Yes" << endl;
    }
    else
    {
        cout <<"No" << endl;
    }
 
    return 0;
}

       2.最富盛名的程序设计竞赛

       世界规模的大赛——Google Code Jam (GCJ)

       Google公司几乎每年都会举办的世界规模的程序设计竞赛,参赛者在2-3小时内解决大约4道题目。

      

       向高排名看齐 —— TopCoder

       一家策划并举办程序设计竞赛的公司,举办比赛设计多个领域。它还有一年一度的TCP公开赛。

      

       历史悠久的竞赛——ACM-ICPC

       美国计算机写会主办的,面向大学生的竞赛,也是历史最悠久的程序设计竞赛。三人一队,要求在5个小时内解决大约10道题目。

      

       面向中学生的信息学奥林匹克竞赛——JOI-IOI

      

       通过网络自动评测——Online Judge(OJ)

       PKUOnline Judge(POJ) —— http://poj.org      题库中有大量题目

       ShereOnline Judge(SPOJ) ——http://www.spoj.pl    允许使用各种各样的语言

       SGUOnline Contester——http://acm.sgu.ru              具有模拟参加历史比赛的虚拟赛功能

       UVaOnline Judge——http://uva.onlinejudge.org

       Codeforces——http://codeforces.com   与TopCoder一样定期举办比赛,且维护历年题库

      

       3.以高效算法为目标

       运行时间的估算,可以从复杂度进行估算,如果估算的复杂度为O(n),那么将最大的n带进去,就得到了运行时间的上限,只要上限不超即可。

 

       简单题目开始:有n根棍子,棍子i的长度为ai,想要从中选出3根棍子组成周长尽可能长的三角形,请输出最大的周长,若无法组成三角形则输出0。

       例如:

       输入:

              n= 5

              a= { 2, 3, 4, 5, 10}

       输出:

              12(选择3,4,5时)

      

       输入:

              n= 4

              a= {4, 5, 10, 20}

       输出:

              0(无论怎么选都无法组成三角形)

      

#include<iostream>
using namespace std;
 
int main()
{
    int n = 5;
    int a[] = { 2, 3, 4, 5,10};
 
    int maxSum = 0;
    for( int i = 0; i < n;i++)
    {
        for( int j = i+1; j< n; j++)
        {
            for( int k = j+1;k < n; k++)
            {
                int sum = a[i]+ a[j] + a[k];
                int big = max(a[i], max(a[j], a[k]));
 
                if( sum - big> big && sum > maxSum)
                {
                    maxSum =sum;
                }
            }
        }
    }
 
    cout << maxSum<< endl;
 
    return 0;
}

 

       POJ的题目Ants:n只蚂蚁以每秒1cm的速度在长为Lcm的杆子上爬行。当蚂蚁爬到杆子的短点时就会掉落下来。由于杆子太细了,两只蚂蚁相遇时,他们不能交错通过,而是只能各自反向爬回去,对于每一只蚂蚁,我们知道它距离杆子最短的距离xi,但不知道它当前的爬向,请计算所有蚂蚁落下杆子所需要的最短时间和最长时间。

       输入:

              L= 10

              n= 3

              x= {2, 6, 7}

       输出:

              min= 4 (左,右,右)

              max= 8 (右,右,右)

 

       第一种方法:

       最容易想到的就是穷举法,即枚举所有蚂蚁的初始朝向的组合,利用递归函数实现。

       这样可以知道,每只蚂蚁的初始朝向有两种可能,那么n值蚂蚁就是2 x 2 x 2 ……x 2 =2^n种,当n较小时可以解决。如果n过大,则无法处理。

       第二种方法:

       首先考虑最短时间,我们可知所有蚂蚁都朝向较近的端点走会比较好。事实上,这种情况下,不会发生两只蚂蚁相遇的情况,而且不可能在比此更短的时间内走到杆子的端点。

       在考虑最长时间的情况。当蚂蚁相遇之后,蚂蚁都将反向继续运动。如果不考虑蚂蚁之间的差异性,那么就相当于是两个蚂蚁交错通过后继续运动(蚂蚁的运动速度相同)。这样就可以得到所有的蚂蚁运动其实都是独立的,所以要求最长时间,只要求出蚂蚁到杆子端点的最大距离就好了。

       那么求最短时间和最长时间变为:所有蚂蚁中每个蚂蚁运动到端点的最短时间中的最长时间 和 所有蚂蚁中每个蚂蚁运动到端点的最长时间中的最长时间

#include <iostream>
using namespace std;
 
int main()
{
    int L = 10;
    int n = 3;
    int x[] = { 2, 6, 7};
 
    int mintime = 0;
    int maxtime = 0;
    for( int i = 0; i < n;i++)
    {
        mintime = max(mintime,min(x[i], L-x[i]));
 
        maxtime = max(maxtime,max(x[i], L-x[i]));
    }
 
    cout << "min=" << mintime << endl;
    cout << "max=" << maxtime << endl;
 
    return 0;
}

       难度增加的抽签问题

       如果将前面抽取n次和为m的题目的限制条件改为 1<= n <= 1000,那么应该如何求解?最初的四重循环的时间复杂度为O(n^4),如果n=1000,那么复杂度为10^12,这样就过于复杂了。

       1.对于四重循环如下:

for( int a = 0; a < n; a++)
{ 
    for(int b = 0; b < n; b++) 
    { 
for(int c = 0; c < n; c++) { for(int d = 0; d < n; d++)
{ if( k[a] + k[b] + k[c] +k[d] == m) {f = true; } } }
    }
}


       最内层是验证k[a] + k[b] + k[c] + k[d] == m,通过处理式子可知,k[d] = m – (k[a] + k[b] +k[c])。就是说最内层循环可变为 判断是否存在m – (k[a] + k[b] + k[c])值,这样可以在最内层采取快速搜索的方法。例如采用二分搜索的方法。二分搜索的时间复杂度为O(logn)。
       这样:
       排序时间O(nlogn)
       循环时间 O(n^3 * logn)
       那么总的时间复杂度变为了O(logn*n^3):

#include<iostream>
using namespace std;
//
bool bsearch(int k[], int x, int n)
{
    int l = 0, r = n;
 
    while(r > l)
    {
        int i = (l + r) / 2;
        if( k[i] == x)
        {
            return true;
        }
        else if(k[i] < x)
        {
            l = i + 1;
        }
        else
        {
            r = i;
        }
    }
 
    return false;
}
 
int main()
{
    int n = 3;
    int m = 10;
    int x[] = { 1, 3, 5};
 
    sort(x, x+n);
 
    bool f = false;
 
    for(int a = 0; a < n;a++)
    {
        for(int b = 0; b <n; b++)
        {
            for(int c = 0; c< n; c++)
            {
                if(bsearch( x,m-x[a]-x[b]-x[c], n))
                {
                    f = true;
                }
            }
        }
    }
 
    if( f)
    {
        cout << "Yes" <<endl;
    }
    else
        cout <<"No" << endl;
 
    return 0;
}

      2. O(n^2 * logn)的算法
       按照1中的思路,我们可已经内层的两个循环一起考虑,也即k[d] + k[c] = m – (k[a] + k[b])。这个时候就不能直接使用二分搜索了,但是我们如果预先枚举出k[d] + k[c]所得到的n^2个数字,并且排好序,便可以使用二分搜索了。
       此时:
       排序时间 O(n^2 * 2logn),常数省略:O(n^2 * logn)
       循环时间 O(n^2 * logn)
       总时间也就是O(n^2 * logn):

#include<iostream>
using namespace std;
 
#define MAX_N 3
//
bool bsearch(int k[], int x, int n)
{
    int l = 0, r = n;
 
    while(r > l)
    {
        int i = (l + r) / 2;
        if( k[i] == x)
        {
            return true;
        }
        else if(k[i] < x)
        {
            l = i + 1;
        }
        else
        {
            r = i;
        }
    }
 
    return false;
}
 
int main()
{
    int n = MAX_N;
    int m = 10;
    int x[] = { 1, 3, 5};
    int xx[MAX_N * MAX_N];
 
    for( int c = 0; c < n;c++)
    {
        for( int d = 0; d <n; d++)
        {
            xx[c*n + d] = x[c]+ x[d];
        }
    }
 
    sort( xx, xx + n*n);
 
    bool f = false;
    for(int a = 0; a < n;a++)
    {
        for(int b = 0; b <n; b++)
        {
            if(bsearch( xx,m-x[a]-x[b], n*n))
            {
                f = true;
            }
        }
 
    }
 
    if( f)
    {
        cout <<"Yes" << endl;
    }
    else
        cout << "No" << endl;
 
    return 0;
}

By  Andy @ 2013年7月22日

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值