习题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;
}