算法竞赛入门经典第二版第三章习题

时间:2023-02-26 23:04:50

写这个的原因是看到一位大神的习题答案总结,于是自己心血来潮也想写一个这个,目的主要是督促自己刷题吧,毕竟自己太弱了。

习题3-1 得分 UVa 1585

大致就是设置一个变量记录到当前为止的连续的O的数量,碰到X就变0,水题。

#include<stdio.h>
#include<ctype.h>
#include<string.h>
char s[90];
int main(void)
{
    int length,n,sum,num;
    scanf("%d",&n);
    while(n--)
    {
        sum=num=0;
        scanf("%s",s);
        length=strlen(s);
        for(int i=0;i<length;i++)
        {
            if(s[i]=='X')
            {
                num=0;
                continue;
            }
            num++;
            sum+=num;
        }
        printf("%d\n",sum);


    }
    return 0;
}

习题3-2 分子量 UV1586
很简单的一个字符串处理。我这里用到了ctype.h里的isupper()函数,还要注意单独一个元素符号后面没有数字的情况。

#include<stdio.h>
#include<ctype.h>
#include<string.h>
#define tan 12.01
#define qing 1.008
#define yang 16.00
#define dan 14.01
char s[90];
int main(void)
{
    int i,length,n,temp;
    double sum;
    scanf("%d",&n);
    while(n--)
    {
        sum=0;

        scanf("%s",s);
        length=strlen(s);
        if(isupper(s[length-1]))
        {
            s[length]='1';
            s[length+1]='\0';
        }
        length=strlen(s);
        for(i=0;i<length-1;i++)
        {
            if(isupper(s[i]) &&isupper(s[i+1]))
            {
                switch(s[i])
                {
                    case 'C':sum+=tan;
                             break;
                    case 'H':sum+=qing;
                             break;
                    case 'O':sum+=yang;
                             break;
                    case 'N':sum+=dan;
                             break;

                }
            }
            if(isupper(s[i]) &&!(isupper(s[i+1])))
            {
                int j=i+1;
                while(!(isupper(s[j]))&&(j<length))
                    j++;
                int myk,weishu=1;
                temp=0;
                for(myk=j-1;myk>=i+1;myk--)
                {
                    temp+=weishu*(s[myk]-'0');
                    weishu*=10;
                }
                switch(s[i])
                {
                    case 'C':sum+=tan*temp;
                             break;
                    case 'H':sum+=qing*temp;
                             break;
                    case 'O':sum+=yang*temp;
                             break;
                    case 'N':sum+=dan*temp;
                             break;

                }
            }


        }
        printf("%.3lf\n",sum);
    }
    return 0;
}

习题3-3 数数字 UVa1225
水题,数据不大,搞个数组就ok。注意数字字符到int类型的转换。

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

char a[40000];
char temp[10];

int main(void)
{
    int t,i,n;
    int num[10];
    scanf("%d",&t);
    while(t--)
    {
        a[0]='\0';
        scanf("%d",&n);
        for( i=1;i<=n;i++)
        {
            sprintf(temp,"%d",i);
            strcat(a,temp);
        }

        int length;
        length=strlen(a);
        for(i=0;i<10;i++)
            num[i]=0;
        for(i=0;i<length;i++)
            num[(a[i]-'0')]++;
        for(i=0;i<9;i++)
            printf("%d ",num[i]);
        printf("%d",num[9]);
        printf("\n");

    }
    return 0;
}

习题3-4 周期串 UVa455

数据量不大,直接暴力枚举周期,用总长度是周期的倍数剔除一部分,然后遍历全串,用%映射到周期中进行比较。

#include<stdio.h>
#include<ctype.h>
#include<string.h>
char a[90];

int main(void)
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
        scanf("%s",a);
        int length;
        length=strlen(a);
        int i,j,status;

        for(i=0;i<length;i++)
        {
            status=1;
            if(length%(i+1)!=0)
                continue;
            for(j=0;j<length;j++)
            {
                if(a[j]!=a[j%(i+1)])
                    status=0;
            }
            if(status==1)
            {
                if(n==0)
                    printf("%d\n",i+1);
                else printf("%d\n\n",i+1);

                break;
            }

        }
    }
    return 0;
}

习题3-5 谜题 UVa227
非常烦的模拟,记得做了好久,注意边界处理和读入(用字符串读比较好,字符格式读入会读入回车符导致意想不到的错误)

#include<stdio.h>
#include<ctype.h>
#include<string.h>
char a[10][10];
int cnt=0;
bool panduan(int i,int j)
{
    if((i>=0)&&(i<5)&&(j>=0)&&(j<5))
        return true;
    else return false;
}
void shuchu(void)
{
    int k,m;
    printf("\n");
    for(k=0;k<5;k++)
    {
        for(m=0;m<5;m++)
        {
            printf("%c",a[k][m]);
        }
        printf("\n");
    }
    printf("\n");
}
int main(void)
{
    char temp,temp1,temp2;
    int i,j,status,temp_x,temp_y,num=1;
    while((temp=getchar())&&(temp!='Z'))
    {
        if(temp=='\n')
            continue;

        a[0][0]=temp;
        if(temp==' ')
            temp_x=temp_y=0;

        for(i=0;i<5;i++)
            for(j=0;j<=5;j++)
            {

                if((i==0)&&(j==0))
                    continue;
                temp2=getchar();
                if(temp2=='\n')
                    continue;
                a[i][j]=temp2;

                if(temp2==' ')
                {

                    temp_x=i;
                    temp_y=j;
                }


            }
        status=1;


        //printf("\nNow temp_x=%d and temp_y=%d\n",temp_x,temp_y);
        while((temp1=getchar()) &&(temp1!='0'))
        {

            switch(temp1)
            {
                case 'A':

                             if(temp_x<=0)
                                status=0;
                             else
                             {
                                 a[temp_x][temp_y]=a[temp_x-1][temp_y];
                                 a[--temp_x][temp_y]=' ';
                             }
                             //shuchu();
                             break;

                case 'B':

                             if(temp_x>=4)
                                status=0;
                             else
                             {
                                 a[temp_x][temp_y]=a[temp_x+1][temp_y];
                                 a[++temp_x][temp_y]=' ';
                             }
                             //shuchu();
                             break;

                case 'L':

                             if(temp_y<=0)
                                status=0;
                             else
                             {
                                 a[temp_x][temp_y]=a[temp_x][temp_y-1];
                                 a[temp_x][--temp_y]=' ';
                             }
                             //shuchu();
                             break;

                case 'R':

                             if(temp_y>=4)
                                status=0;
                             else
                             {
                                  a[temp_x][temp_y]=a[temp_x][temp_y+1];
                                  a[temp_x][++temp_y]=' ';
                             }
                             //shuchu();
                             break;
                case '\n':
                             break;

                default:status=0;
                        break;

            }
            //printf("Now the step is %c and status=%d\n",temp1,status);


        }
        if(cnt++)
            printf("\n");
        printf("Puzzle #%d:\n",num++);
        if(status==0)
            printf("This puzzle has no final configuration.\n");
        else
        {
            for(i=0;i<5;i++)
            {
                for(j=0;j<4;j++)
                {
                    printf("%c ",a[i][j]);
                }
                printf("%c",a[i][4]);
                printf("\n");
            }

        }

    }
    return 0;

}

习题3-6 纵横字谜的答案 UVa232
又是一道模拟。模拟可难可不难,关键是静下心来做。
首先是输入输出格式,一般一道题目简单了就会在这种地方设卡。这道题的换行要求比较严格,不能有多的换行。解决方法很简单,就是每次输出前输出换行,忽略第一次就好了。

然后是我的算法的一个bug,非常难发现,是一个越界错误,是for循环导致的。所以在以后for的循环变量要运用的时候一定要小心!最后找了一个正确程序,写了批处理对拍才AC。

对拍、生成数据可以看这个博客:

http://blog.csdn.net/churehill123/article/details/19647579

我的算法很智障,就是找到单词的出发点,结束点。

#include<stdio.h>
#include<string.h>
int r,c;
char a[12][12];
char temp_input[13];
int b[12][12];//to record the number of each white grid(0 when black)
int iseligible(int x,int y)
{
    if(a[x][y]=='*')
        return 0;
    if((x==1)||(y==1))
        return 1;
    if((a[x][y-1]=='*')||(a[x-1][y]=='*'))
        return 1;
    else
        return 0;
}
void fuzhi(void)
{
    int temp=0;
    for(int i=1;i<=r;i++)
        for(int j=1;j<=c;j++)
    {
        if(iseligible(i,j))
        {
            temp++;
            b[i][j]=temp;
        }
    }
}
void row(void)
{
    int start_position,end_position;
    printf("Across\n");
    for(int i=1;i<=r;i++)
        for(int j=1;j<=c;j++)
    {
        start_position=j;
        if(!iseligible(i,j))
            continue;
        if((j!=1)&&(a[i][j-1]!='*'))
            continue;
        for(end_position=start_position;end_position<=c;end_position++)
        {
            if(a[i][end_position]=='*')
            {
                end_position--;
                break;
            }
        }
        if(end_position>c)
            end_position=c;
        printf("%3d.",b[i][start_position]);
        for(int k=start_position;k<=end_position;k++)
            printf("%c",a[i][k]);
        printf("\n");

    }
}
void column(void)
{
    int start_position,end_position;
    printf("Down\n");
    for(int i=1;i<=r;i++)
        for(int j=1;j<=c;j++)
    {
        start_position=i;
        if(!iseligible(i,j))
            continue;
        if((i!=1)&&(a[i-1][j]!='*'))
            continue;
        for(end_position=start_position;end_position<=r;end_position++)
        {
            if(a[end_position][j]=='*')
            {
                end_position--;
                break;
            }
        }
        if(end_position>r)
            end_position=r;
        printf("%3d.",b[start_position][j]);
        for(int k=start_position;k<=end_position;k++)
            printf("%c",a[k][j]);
        printf("\n");

    }
}
int main(void)
{
    //freopen("in.txt","r",stdin);
    //freopen("outwrong.txt","w",stdout);
    int num_puzzle=0;
    while(scanf("%d",&r)&&(r!=0))
    {
        if(num_puzzle)
            printf("\n");
        memset(b,0,sizeof(0));
        scanf("%d",&c);
        num_puzzle++;
        for(int i=1;i<=r;i++)
        {
            scanf("%s",temp_input);
            for(int j=1;j<=c;j++)
                a[i][j]=temp_input[j-1];
        }
        fuzhi();//to allowcate different numbers of each eligible white grid
        printf("puzzle #%d:\n",num_puzzle);
        row();//to print the across words
        column();

    }
    return 0;
}

习题3-7 DNA序列 UVa1368
这道题很有意思,一开始我都没想到做法。其实很简单:题目要求是Hamming距离和最小,又因为每个字符串都是由固定的TAGC组成的,所以我们可以统计每一位出现的次数,出现次数最多的就是这一位的字母。另外要注意出现次数相同时,按照题目要求,字典序排列。

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int a[1010][5],b[1010];//a stands for the frequency each alphabet occurs in each position,b stands for the answer string
//a b starts from 1
int n,m,ans;
char temp[1010];
int chartonum(char x)
{
    switch (x)
    {
        case 'A':return 1;
        case 'C':return 2;
        case 'G':return 3;
        case 'T':return 4;
    }
}
char numtochar(int x)
{
    switch (x)
    {
        case 1:return 'A';
        case 2:return 'C';
        case 3:return 'G';
        case 4:return 'T';
    }
}
void duru(void)
{
    int temp_num;
    scanf("%s",temp);
    for(int i=0;i<n;i++)
    {
        temp_num=chartonum(temp[i]);
        a[i+1][temp_num]++;
    }


}
void bijiao(int x)
{
    int temp_zuida=a[x][1];
    int index_zuida=1;
    for(int i=2;i<=4;i++)
    {
        if(a[x][i]>temp_zuida)
        {
            temp_zuida=a[x][i];
            index_zuida=i;
        }
    }
    ans+=a[x][1]+a[x][2]+a[x][3]+a[x][4]-a[x][index_zuida];
    b[x]=index_zuida;
}
int main(void)
{
    int casenum;
    scanf("%d",&casenum);
    while(casenum--)
    {
        memset(a,0,sizeof(a));

        ans=0;
        scanf("%d%d",&m,&n);
        for(int i=0;i<m;i++)
            duru();
        for(int i=1;i<=n;i++)
        {
            bijiao(i);
        }
        for(int i=1;i<=n;i++)
        {
            printf("%c",numtochar(b[i]));
        }
        printf("\n%d\n",ans);

    }
    return 0;
}

习题3-8 循环小数 Uva202
这题可以说做的我心力交瘁,但是理清思路之后其实不难。这道题做的真的是很吃力,一开始没有思路,看了别人的博客开始瞎几把乱写,最后终于没有做出来。
于是全部推翻重来,终于AC了。
首先是除法的基本原理:保存a/b然后a=a%b*10,不停重复直到出现循环节。
如何判断出现循环节呢?
思路是存储一个status数组,存放每次a%b出现的位置,赋初值为-1。注意算整数求出来的a%b也要存!存成status【a%b】=0;然后每次运算检查status【a%b】是否已经有值了。假如status【a%b】已经等于k,现在是第m位,则第k+1位到第m位是循环节。

#include<iostream>
#include<cstring>
#include<climits>
#include<cmath>
#include<algorithm>
using namespace std;
int xs[10000];
int main(void)
{
    int aa,k,m,temp,a,b,zs,num_repeat,index_repeat;
    int status[3010];
    while(cin>>a>>b)
    {
        aa=a;
        zs=a/b;
        memset(status,-1,sizeof(status));
        status[a%b]=0;
        a=a%b*10;
        for(int i=1;i<3010;i++)
        {
            xs[i]=a/b;
            temp=a%b;
            if(status[temp]==-1)
            {
                status[temp]=i;
                a=a%b*10;

            }
            else
            {

                k=status[temp];
                m=i;
                break;
            }
        }
        //printf("m=%d and k=%d\n",m,k);
        if(m-k>50)
        {
            printf("%d/%d = %d.",aa,b,zs);
            for(int i=1;i<=k;i++)
                printf("%d",xs[i]);
            printf("(");
            for(int i=k+1;i<=k+50;i++)
                printf("%d",xs[i]);
            printf("...)\n");
        }
        else
        {
            printf("%d/%d = %d.",aa,b,zs);
            for(int i=1;i<=k;i++)
                printf("%d",xs[i]);
            printf("(");
            for(int i=k+1;i<=m;i++)
                printf("%d",xs[i]);
            printf(")\n");
        }
        printf(" %d = number of digits in repeating cycle\n\n",m-k);
    }
    return 0;
}

习题3-9 子序列 UVa10340
这是一道非常简单的字符串匹配题目,然而因为我太菜,看了题解才会做。
仔细说一下算法:
假设要查找的子串为“ab”。则第一步,在长串中寻找a的位置(第一次出现);第二步,从这个位置往后,搜索b的位置。搜索到b则ab是该长串的子串,反之则不是。
当子串更长时,同样的,子串从左到右每个位置都搜索一遍,关键是“从这个位置往后”。
为什么呢?
首先,这个算法保证了子串里每个字符都在长串里出现了,其次,它保证了这些字符出现的顺序是正确的:假设在子串中b在a后面,因为我们搜索的是长串中第一个出现的a的位置,所以在这以后搜索到的b(如果搜的到)肯定是在至少一个a后面的。

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
char s[100000+10],t[100000+10];
int main(void)
{
    int flag,length_s,length_t;
    while(scanf("%s%s",s,t)!=EOF)
    {
        length_s=strlen(s);
        length_t=strlen(t);
        flag=0;
        for(int i=0;i<length_t;i++)
        {
            if(t[i]==s[flag])
            {
                flag++;
            }
        }
        if(flag==length_s)
            printf("Yes\n");
        else
            printf("No\n");
    }
    return 0;
}

习题3-10 盒子 UVa1587
注意多组数据。这道题我写烦了,基本思路就是判断是不是满足盒子的条件:4*3条边(边可以相等)

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
using namespace std;
struct myk{
int chang;
int kuan;};
myk a[10];
bool cmp1(myk x,myk y)//以长优先排序
{
    if(x.chang!=y.chang)
        return(x.chang<y.chang);
    else
        return(x.kuan<y.kuan);
}
int main(void)
{
    int ans,num;
    int b[20];
    while(1)
    {
        ans=1;
        for(int i=1;i<=6;i++)
            if(scanf("%d%d",&a[i].chang,&a[i].kuan)==EOF)
                goto myk;
        num=0;
        for(int i=1;i<=6;i++)
        {
            if(a[i].chang<a[i].kuan)
                swap(a[i].chang,a[i].kuan);
        }
        sort(a+1,a+7,cmp1);
        /* for(int i=1;i<=6;i++) printf("%d %d\n",a[i].chang,a[i].kuan); */
        for(int i=1;i<=3;i++)
        {
            if((a[2*i-1].chang!=a[2*i].chang)||(a[2*i-1].kuan!=a[2*i].kuan))
                ans=0;
        }

        for(int i=1;i<=3;i++)
        {
            b[num++]=a[2*i].chang;
            b[num++]=a[i*2].kuan;
        }
        sort(b,b+6);

        if((b[0]!=b[1])||(b[2]!=b[3])||(b[4]!=b[5]))
            ans=0;
        //printf("ans=%d\n",ans);
        if(ans)
            printf("POSSIBLE\n");
        else
            printf("IMPOSSIBLE\n");
    }
    myk:return 0;
}

习题3-11换抵挡装置 UVa1588
题目有点难理解,其实可以把它理解成有n1,n2根棍子,棍子可以互相碰到一起,但是高度不能超过3,有点像高中的木块摩擦问题。就是两个木块相互滑动。其实在脑子里想一想这个模型,就可以避免一个常犯错误,就是小木块往左移动。小木块往左移动可以等效为大木块向右移动。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<limits.h>
#include<math.h>
#include<algorithm>
using namespace std;
int a[300],b[300];
char aa[210],bb[210];
int main(void)
{
    //freopen("in.txt","r",stdin);
    //freopen("out2.txt","w",stdout);
    int ans,ans1,ans2,length_a,length_b,index_a,index_b,status=1;
    while(scanf("%s%s",aa,bb)!=EOF)
    {
        status=1;
        ans1=ans2=INT_MAX;
        length_a=strlen(aa);
        length_b=strlen(bb);
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        for(int i=0;i<length_a;i++)
            a[i]=aa[i]-'0';
        for(int i=0;i<length_b;i++)
            b[i]=bb[i]-'0';

        for(int i=0;i<=length_a;i++)
        {
            status=1;
            for(index_a=i,index_b=0;index_b<100;index_a++,index_b++)
            {
                if(a[index_a]+b[index_b]>3)
                {
                    status=0;
                    break;
                }
            }
            if(status==1)
            {
                ans1=max(length_a,i+length_b);
                break;
            }
        }
        for(int i=0;i<=length_b;i++)
        {
            status=1;
            for(index_b=i,index_a=0;index_a<100;index_a++,index_b++)
            if(a[index_a]+b[index_b]>3)
            {
                status=0;
                break;
            }
                if(status==1)
                {
                    ans2=max(i+length_a,length_b);
                    //printf("Now ans2=%d and i=%d\n",ans2,i);
                    break;
                }

        }
        ans=m
in(ans1,ans2);
        cout<<ans<<endl;
    }
    return 0;
}

习题3-12 浮点数 UVa11809
这道题看了题解,羞愧。。。做法是取对数,最后的精度要注意,太大太小都不行,我用的1e-4.
题解百度题号,第一篇我觉得写得非常好,我就不重复了。说一说我从这道题里学到的东西:
第一,查表法的运用。本题的答案是有范围的,可以考虑查表。
第二,部分数学公式的熟练运用,如pow,fabs,log等等。
第三,数学方法的运用:等式两边太大而且有指数则开方。
第四,改程序,写程序一定要谨慎,耐下性子,慎重

#include<iostream>
#include<string.h>
#include<limits.h>
#include<float.h>
#include<math.h>
#include<algorithm>
using namespace std;
struct myk{
double aa;
int bb;};
myk chabiao[10][31];
char input[30];
int main(void)
{
    int i,j;
    long double m,e,t,a,b,ans;
    for(int i=0;i<10;i++)
        for(int j=1;j<=30;j++)
    {
        m=1-pow(2.0,-i-1);
        e=pow(2,j)-1;
        t=log10(m)+(log10(2)*e);
        chabiao[i][j].bb=(int)t;
        double temp=t-chabiao[i][j].bb;
        chabiao[i][j].aa=pow(10,temp);
        //printf("chabiao[%d][%d]:%d %d\n",i,j,chabiao[i][j].aa,chabiao[i][j].bb);
    }
    while(scanf("%s",input))
    {
        a=b=0.0;
        if((input[0]=='0')&&(input[1]=='e')&&(input[2]=='0'))
            break;
        a+=input[0]-'0';
        for(int i=2;i<=16;i++)
        {
            a+=(input[i]-'0')*pow(10,1-i);
        }
        for(int i=18;i<strlen(input);i++)
        {
            b+=pow(10,strlen(input)-i-1)*(input[i]-'0');
        }

        //cout<<"a="<<a<<endl<<"b="<<b<<endl;
        for(int i=0;i<10;i++)
            for(int j=1;j<=30;j++)
        {
            if((fabs(chabiao[i][j].aa-a)<1e-4)&&(chabiao[i][j].bb==b))
            {
                printf("%d %d\n",i,j);
                //goto jies
hu;
            }
        }

    }
    return 0;
}

至此,第三章的习题就完成了,大约做了半个月。希望第四章能在5天内完成。