《算法竞赛入门经典(第二版)》第三章 习题(6-9)

本文解析UVa在线编程竞赛中的四道题目,包括纵横字谜、DNA序列、循环小数及子序列问题,提供了详细的解题思路与C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

习题3-6 纵横字谜的答案 (UVa232 Crossword Answers)

解题思路

首先读入整张字谜图,同时根据读入情况设置一个head[i][j][0],里面记录了坐标[i,j]作为起始格的编号。(如果不是则记为0)同时为了区分横行与纵列,利用head[i][j][1]记录是否是横行起始格,用head[i][j][2]记录是否是纵列起始格,然后利用一个循环嵌套while进行输出。

注意

本体数据分组,要求两组之间用空行隔开,并且最后没有多余空行(但是最后一行还是要有换行符的)。UVa中的大多数题都是这个输出要求——然而后面也有几道题是比较坑人的:要求文末有空行。

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;


int main(){
    int r,c,count=1;
    while(scanf("%d %d\n",&r,&c)==2 && r!=0){
        char map[15][15]={{0}};
        int head[15][15][3]={{{0}}}; 
        printf("%spuzzle #%d:\n",count>1?"\n":"",count);
        count++;
        int tmp=1;
        for(int i=0;i<r;i++){
            for(int j=0;j<c;j++){
                scanf("%c",&map[i][j]);
                int flag=0;
                if(map[i][j]!='*'){
                    if(i==0 || map[i-1][j]=='*') 
                        head[i][j][2]=flag=1;
                    if(j==0 || map[i][j-1]=='*')
                        head[i][j][1]=flag=1;
                    if(flag)head[i][j][0]=tmp++;
                }
            }
            getchar();
        }
        printf("Across\n");
        for(int i=0;i<r;i++)
            for(int j=0;j<c;j++)
                if(head[i][j][1]){
                    printf("%3d.",head[i][j][0]);
                    int t2=j;
                    while(t2<c && map[i][t2]!='*')
                        printf("%c",map[i][t2++]);
                    printf("\n");
                }
        printf("Down\n");
        for(int i=0;i<r;i++)
            for(int j=0;j<c;j++)
                if(head[i][j][2]){
                    printf("%3d.",head[i][j][0]);
                    int t1=i;
                    while(t1<r && map[t1][j]!='*')
                        printf("%c",map[t1++][j]);
                    printf("\n");
                }                 
    }
    return 0;

习题3-7 DNA序列 (UVa1368 DNA Consensus String)

解题思路

分析样例数据可知,对于输入的m个DNA序列,每个位置出现的次数最多的碱基就是目标序列的这个位置的碱基,当有出现次数相同的碱基时,取字典序较小的一个。如输入ACGT,ACTG,AGCT,CAGT时,在[0]位置,A出现了3次,C出现了一次,故[0]处取A;在[1]位置,C出现了2次,G和A各出现了1次,故[1]处取C;同理,[2]处取G,[3]处取T,最后的答案序列就应该是ACGT。至于它到其他序列的总距离,就是所有不相同的碱基之和。

#include<string.h>

using namespace std;

 

int ACGTtoN(char ch){

    switch(ch){

        case 'A':return 0;

        case 'C':return 1;

        case 'G':return 2;

        case 'T':return 3;

    }

}

char NtoACGT(int n){

    switch(n){

        case 0:return 'A';

        case 1:return 'C';

        case 2:return 'G';

        case 3:return 'T';    

    }

}

int main(){

    int T;

    scanf("%d",&T);

    while(T--){

        int m,n,ans[1005]={-1},distance=0;

        char input[55][1005]={0};

        scanf("%d %d\n",&m,&n);

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

            gets(input[i]);

        for(int j=0;j<n;j++){

            int tmp[5]={0},max=-1;

            for(int i=0;i<m;i++){

                int tmpn=ACGTtoN(input[i][j]);

                tmp[tmpn]++;

                if(tmp[tmpn]>max || (tmp[tmpn]==max && tmpn<ans[j]))

                    { max=tmp[tmpn]; ans[j]=tmpn;}    

            }

            for(int i=0;i<4;i++){

                if(i==ans[j])continue;

                distance+=tmp[i];

            }

        }    

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

            printf("%c",NtoACGT(ans[i]));

        printf("\n%d\n",distance);

    }

    return 0;

习题3-8 循环小数 (UVa202 Repeating Decimals)

解题思路

在除法计算过程中,除数是不会变的,而被除数始终在变——它等于上一个被除数%除数*10,利用这条关系不断循环求解被除数,当发现这个被除数之前出现过时,则是发现了循环节。在求解被除数的同时,要注意保留被除数/除数得到的余数,方便之后输出。

注意

这道题要求行末有空行!!!!与之前要求无空行不同,这道题要求有空行,我为此查了好久的错……输出时各种东西比较复杂,如果细节方面不清楚的话有必要手动过一遍程序,看看哪些细节会影响输出。

#include<iostream>
#include<string.h>
#include<stdio.h>
using namespace std;

int check(int s[],int n);

int main(){
    const int maxn=10000;
    int n,m;
    while(scanf("%d%d",&n,&m)>0){
        printf("%d/%d = ",n,m);        
        int ans[maxn]={0},mod[maxn]={0},count=1,begin=0;
        while(true){
            mod[count]=n%m;
            ans[count]=n/m;
            if(begin=check(mod,count)) break;
            n=mod[count++]*10;
        }
        for(int i=1;i<=count;i++){
            printf("%d",ans[i]);
            if(i==1)                printf(".");
            if(i==begin)            printf("(");
            if(i-(begin+1)+1==50) { printf("...)\n"); break; }
            if(i==count)            printf(")\n");
        }
        printf("   %d = number of digits in repeating cycle\n\n",count-begin);
    }
    return 0;


int check(int s[],int n){
    for(int i=1;i<n;i++)
        if(s[i]==s[n])return i;
    return 0;
}


习题3-9 子序列 (UVa10340 All in All)

解题思路

题不难,就是遍历一遍字符串t,设置一个p1指向s[0],发现t中的字符等于s[p1]时p1++,最后遍历完t后看p1是否等于s的长度,等于则输出“Yes",否则就输出"No".

注意

我一开始用的是c风格字符串存储s和t,但是发现总是Runtime Error,看了看别人的答案发现换成string类就可以了……至今不知道为什么,很谜。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

const int maxn=10000;
int main(){
    string s,t;
    while(cin>>s>>t){
        int n=t.size(),ns=s.size(),p1=0;
        for(int i=0;i<n;i++){
            if(s[p1]==t[i])
                p1++;
            if(p1==ns)break;
        }
        if(p1==ns)printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值