算法竞赛入门经典~chapter 3 例题

本文深入浅出地介绍了算法竞赛的基础知识,并通过具体例题详细解释了开灯问题、蛇形填数、竖式问题及回文串等算法挑战。文章提供了详细的代码实现,帮助读者理解和解决相关问题。

3-1  开灯问题

有n盏灯,编号为1~n。第一个人把所有打开,第2个人按下所有编号为2的倍数的开关(这些灯将被关掉),第3个人按下所有编号为3的倍数的开关 (其中关掉的灯被打开你,开着的灯将被关闭),一次类推。一共有k个人,问最后又哪些灯开着?输入 :n和k,输出开着的灯编号。k <= n <= 1000.

样例输入:7 3

样例输出:1 5 6 7

Code#include<stdio.h>
#include<string.h>

const int maxn = 1000 + 5;
int main()
{
    int n, k, light[maxn], t = -1;
    while(scanf("%d%d", &n, &k) == 2)
    {
        t = -1;
        memset(light, -1, sizeof(light));   //初始状态都是关着的(1表示开,-1表示关)
        for(int i = 1; i <= k; ++i)
            for(int j = i; j <= n; j += i)
                light[j] *= t;
        for(int i = 1; i <= n; ++i)
            if(light[i] == 1)
                printf("%d ", i);
        printf("\n");
    }
    return 0;
}

 

3-2  蛇形填数(抽象问题)

在n*n方阵里填入1,2,…,n*n,要求填成蛇形。例如4时方阵为:

10  11  12   1
9  16  13   2
8  15  14   3
7   6   5   4

上面的方阵中,多余的空格只是为了便于观察规律,不必严格输出。n <= 8。

从x=0,y=n-1,即第0行,第n-1列。“笔”的移动轨迹是:下,下,下,,左,左,左,上,上,上,右,右,下,下,上。总之,先是下,都不能填了为止,然后是左,接着是上,最后是右。“不能填”是指再走就出界了,或者再走就要走到以前体拿过的格子。如果我们把所有格子初始化为0,就能方百年地加以判断。

Code1#include<stdio.h>
#include<string.h>

const int maxn = 5000+10;
int snag[maxn][maxn];
int main()
{
    int n, x, y;
    while(scanf("%d", &n) == 1)
    {
        memset(snag, 0, sizeof(snag));  //设置标志以方便判断数组是否已经赋值
        int num = snag[x=0][y=n-1]= 1;
        while(num < n*n)
        {
            //抽象为下、左、上、右四个方向的移动,
            while(x+1 < n &&  !snag[x+1][y])    snag[++x][y] = ++num;
            while(y-1 >= 0 && !snag[x][y-1])    snag[x][--y] = ++num;
            while(x-1 >= 0 && !snag[x-1][y])    snag[--x][y] = ++num;
            while(y+1 < n &&  !snag[x][y+1])    snag[x][++y] = ++num;
        }
        for(x = 0; x < n; ++x)
        {
            for(y = 0; y < n; ++y)
                printf("%3d ", snag[x][y]);
            printf("\n");
        }
    }
    return 0;
}
C++_Style:
Code2#include<iostream>
#include<cstring>
using namespace std;

const int maxn = 5000 + 10;
int a[maxn][maxn];//(当数组很大时)数组必须开到外面
int main()
{
    int n;
    while(cin >> n)
    {
        int x, y, cnt;
        memset(a, 0, sizeof(a));
        cnt = a[x=0][y=n-1] = 1;
        while(cnt < n*n)
        {
            while(x+1 < n && !a[x+1][y])    a[++x][y] = ++cnt;
            while(y-1 >= 0 && !a[x][y-1])   a[x][--y] = ++cnt;
            while(x-1 >= 0 && !a[x-1][y])   a[--x][y] = ++cnt;
            while(y+1 < n && !a[x][y+1])    a[x][++y] = ++cnt;
        }
        for(x = 0; x < n; ++x)
        {
            for(y=0; y < n; ++y)
            {
                cout.width(5);
                cout.setf(ios::right);
                cout << a[x][y] << " ";
            }
            cout << endl;
        }
    }
    return 0;
}

3-3  竖式问题

找出所有形如abc*de(三位数乘以两位数)的算式,使得在完整的竖式中,所有数字都属于一个特定的集合。输入数字集合(相邻数字之间没有空格),输出所有竖式。每个竖式前对应有编号,只有应有一个空行。最后输出解的总数。

样例输入:2357

样例输出:

<1>
  775
X  33
-----
2325
2325
-----
25575


The number of solutions = 1


 

Code#include<stdio.h>
#include<string.h>

int main()
{
    int i, ok, abc, de, x, y, z, count;
    char s[20], buf[99];
    //printf("%c\n", 999);?的ASCII码是999.
    while(scanf("%s", s) == 1)
    {
        count = 0;
        for(abc = 111; abc <= 999; abc++)
            for(de = 11; de <= 99; de++)
            {
                x = abc*(de%10);    //x存储与各位相乘的结果
                y = abc*(de/10);    //y存储与市十位相乘的结果
                z = abc*de;         //z存储最终结果
                sprintf(buf, "%d%d%d%d%d", abc, de, x, y, z);
                ok = 1;
                for(i = 0; i < strlen(buf); i++)
                    if(strchr(s, buf[i]) == NULL)//如果在s中buf[i]不存在,ok置0
                        ok = 0;
                if(ok)
                {
                    printf("<%d>\n", ++count);
                    printf("%5d\nX%4d\n-----\n%5d\n%4d\n-----\n%5d\n\n", abc, de, x, y, z);
                }
            }
        printf("The number of solutions = %d\n", count);
    }
    return 0;
}


3-4 回文串(编码阶段可以采用迭代式开发:每次只实现一点小功能,但要充分测试,确保它工作正常。)

输入一个字符串,求出其中最长的回文子串。字串的含义是:在原串中连续出现的字符串片段。回文的含义是:正着看和倒着看相同,如abba和yyxyy。在判断时,应该忽略suoyou8标点符号和空格,且忽略大小写,单输出应保持鸳鸯(在回文串的首部和尾部不要输出多余字符)。输入字符串长度不超过5000,且占据单独一行。应该输出最长的回文串,如果有多个,输出起始位置最靠左的。

样例输入:Cofuciuss say:Madam,i'm Adam.

样例输出:Madam, i'm Adam

Code#include<stdio.h>
#include<string.h>
#include<ctype.h>
const int maxn = 5000+10;
char buf[maxn], str[maxn];
int sub[maxn];
int main()
{
    while(fgets(buf, maxn, stdin))
    {
        int len = strlen(buf), cnt = 0;
        for(int i = 0; i < len; ++i)
            if(isalpha(buf[i]))
            {
                sub[cnt] = i;   //存入下标
                str[cnt++] = toupper(buf[i]);
            }
        int max = 0, beg, end;
        for(int i = 0; i < cnt; ++i)
        {
            for(int j = 0; i-j >= 0 && i+j < cnt; ++j)
            {
                //偶数个
                if(str[i-j] != str[i+j])     break;
                if(2*j+1 > max)
                {
                    max = 2*j + 1;
                    beg = sub[i-j];
                    end = sub[i+j];
                }
            }
            for(int j = 0; i-j >= 0 && i+j+1 < cnt; ++j)
            {
                //奇数个
                if(str[i-j] != str[i+j+1])     break;
                if(2*j+2 > max)
                {
                    max = 2*j + 2;
                    beg = sub[i-j];
                    end = sub[i+j+1];
                }
            }
        }
        for(int i = beg; i <= end; ++i)
            putchar(buf[i]);
        putchar('\n');
    }
    return 0;
}



/*


 


 

posted on 2012-12-19 22:43 桑海 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/sanghai/archive/2012/12/19/2825639.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值