算法竞赛入门经典(第2版)-刘汝佳-第三章解题源码(C语言)

习题3-1


#include<stdio.h>
#include<string.h>
int main(){
	int lenth,n;
	char s[100];
	scanf("%d",&n);
		while(n--){
			scanf("%s",s);
   	 		lenth=strlen(s);
   	 		int score=0;
   	 		int currento=0;
   	 		for(int i=0;i<lenth;i++)
    			if (s[i]=='O'){
    				currento++;
    				score+=currento;
    			}
				else
					currento=0;	
	
			printf("%d\n",score);
		}
	return 0;
	
} 

习题3-2

#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<math.h>
int main(){
	int n;
	scanf("%d",&n);
	while(n--)
	{
		char s[50],ds[50];	
		double sum=0,tmp=0;
		scanf("%s",s);
		int lenth = strlen(s);
		for(int i=0;i<lenth;){
			switch(s[i]){
				case 'C':
						tmp=12.01;
						break;
				case 'H':
						tmp=1.008;
						break;		
				case 'O':
						tmp=16.00;
						break;
				case 'N':
						tmp=14.01;
						break;		
				
			}
			int j=i+1,k=0,l=0,cd=0;
			if(isdigit(s[j])){
			
			while (isdigit(s[j])){
				ds[k++]=s[j++];	
			}
			for(int m=1;m<=k;m++){
				cd+=int(ds[k-m]-'0')*pow(10,l++);
		    }
			sum+=cd*tmp;
			i=i+k+1;
		}
			else
			{
			sum+=tmp;
			i++;
			}
		}
		printf("%.3f\n",sum);					
	}	
	return 0;
}

习题3-3

#include<stdio.h>
#include<string.h>
int main()
{
	int n,m,ans[10];
	scanf("%d",&n);
	
	while(n--)
	{
		memset(ans,0,sizeof(ans));
		scanf("%d",&m);
		for(int i=1;i<=m;i++)
		{
			int x=i,y;
			while(x){
				y=x%10;
				ans[y]++;
				x=x/10;
			}
		}
		for(int i=0;i<9;i++)
		printf("%d ",ans[i]);
		printf("%d\n",ans[9]);
		
	}
	
	return 0;
}

习题3-4

  本题目一定要注意输出格式。

#include<stdio.h>
#include<string.h>
int main()
{
	int n;
	scanf("%d",&n);
	while(n--)
	{
		char s[100];		
		scanf("%s",s);
		int lenth=strlen(s);
		int i=1;
		for (i=1;i<=lenth;i++)
		{		  
			int flag=1;  
			if(lenth%i==0){	
				char substring[100];
		   		for(int j=0;j<i;j++)
		   		substring[j]=s[j];
				for(int m=0;m<lenth;m++)
					if(substring[m%i]!=s[m])
					{
						flag=0;
						m=lenth+1;
					}			
				}
			else 
				flag=0;
		
			if(flag)
				{
					printf("%d\n",i);
					i=lenth+1;	
					if(n) printf("\n");//输出格式注意
				}						
			}		
		}
	return 0;
}

习题3-5

注意空格和回车的输入,输出格式也要注意,算法本身并不困难。

#include<stdio.h>
#include<string.h>
int main()
{
	char puzzle[5][5],c,ins[100];
	int puzzlei=1;//失败为假。
	while((c=getchar())!='Z'){
		int fail=0;
		int x,y;//记录空白格的位置。
		if(puzzlei!=1)
		{			
			printf("\n");//输出格式控制 
		}
		printf("Puzzle #%d:\n",puzzlei++);
		puzzle[0][0]=c;
		if(c==' ')
		{	x=0;
			y=0;		
		}
		
		
		int i=0,j=1,n=5;
		while(n)
		{
			if((c=getchar())!='\n')
			{
				puzzle[i][j]=c;
				if(puzzle[i][j]==' ')
				{
					x=i;
					y=j;
				}
				j++;	
			}
					
			else
			{
				i=i+1;
				j=0;
				n--;
			}
					
		}
								
		while((c=getchar())!='0')
		{
			switch(c)
			{
				case 'A':
				{
					if(x-1>=0)
					{
					puzzle[x][y]=puzzle[x-1][y];
					puzzle[x-1][y]=' ';	
					x=x-1;
					}
					else
					fail=1;					
					break;
				}
					
				case 'B':
					{
						if(x+1<5)
						{
							puzzle[x][y]=puzzle[x+1][y];
							puzzle[x+1][y]=' ';	
							x=x+1;
						}
						else
							fail=1;		
						break;							
					}
					
				case 'L':
					{
						if(y-1>=0)
						{
							puzzle[x][y]=puzzle[x][y-1];
							puzzle[x][y-1]=' ';	
							y=y-1;
						}
						else
							fail=1;						
						break;						
					}	
				case 'R':
					{
						if(y+1<5)
						{
							puzzle[x][y]=puzzle[x][y+1];
							puzzle[x][y+1]=' ';	
							y=y+1;
						}
						else
							fail=1;						
						break;	
					}
				case '\n':
					break;
				case '\r':
					break;
				default:
					fail=1; 
					break;				
			}
		
		}
		getchar();//接收0后面的回车。 
		if(fail)
		{
			printf("This puzzle has no final configuration.\n");
		}
		else
		{
			for(int i=0;i<5;i++)		
			{	
				for(int j=0;j<5;j++)
				{
			   		printf("%c",puzzle[i][j]);
			   	    if(j<4)
					   {
					   	printf(" ");
					   }
				}
				printf("\n");				
			}
			
	
		}
			
	}
	
	
	return 0;
} 

习题3-6

注意点:

1、输出的答案最后一行只换了一行,有可能会输出两行。

2、注意输入时候,回车空格,scanf都会接收,通过增加额外的scanf避免回车输入进数据数组中。

3、如果要通过文件测试,在windows系统的文本文档中,换行的表示为'/r/n',而不是'/n'。

#include<stdio.h>
#include<string.h>
int main(){
	int r,c,puzzlei=1;	
	//FILE *fp,*fp2;
	//fp=fopen("datain.txt","rb");
	//fp2=fopen("dataout.txt","wb");
	while(scanf("%d",&r)&&r!=0)
	{	if(puzzlei!=1)
	    printf("\n");
		int sqn=1,flag=1,i=0,j=0,sum;
		char input[15][15];
	    int  sn[15][15];
	    memset(sn,0,sizeof(sn));
		char tmp;
		scanf("%d",&c);
		printf("puzzle #%d:\n",puzzlei++);
		sum=r*(c+1);//使用文件测试的时候需要准备/r/n两个字符 
		scanf("%c",&tmp);//接收r,c后的回车 
		//fscanf("%c",&tmp);接收文件的/r 
			while(sum--)//主要是要处理输入的回车 
			{
				
				scanf("%c",&tmp);
				if(tmp!='\n'&&tmp!='\r')
				{
					input[i][j]=tmp;
					if(input[i][j]!='*')
					{
						if(j-1<0||i-1<0||input[i][j-1]=='*'||input[i-1][j]=='*')
						sn[i][j]=sqn++;
					}
					j++;		
				}
				else if(tmp=='\n')
				{
					i++;
					j=0;
				}
			}							
		    printf("Across\n");
			for(int i=0;i<r;i++)//横向输出 
			{	
				for(int j=0;j<c;j++)
				{
					
					if(input[i][j]!='*')
					{				
						if(flag==1)
						{
							printf(" ");
							if(sn[i][j]<10)
								printf(" ");
							printf("%d.",sn[i][j]);	
							flag=0;
						}
						printf("%c",input[i][j]);
					
						if(input[i][j+1]=='*'||j+1==c)
						{
							flag=1;
							printf("\n");
						}
					}
				
				
					
				}
			}
			
			printf("Down\n");
			flag=1;
			for(int i=0;i<r;i++)//纵向输出使用 
			{	
				for(int j=0;j<c;j++)
				{
					if(sn[i][j]!=0)
					{
						if(i-1<0||input[i-1][j]=='*')
						{
							printf(" ");
							if(sn[i][j]<10)
								printf(" ");
							printf("%d.",sn[i][j]);	
							int k=i;
							while(input[k][j]!='*'&&k<r)
							{
								printf("%c",input[k][j]);
								k++;	
							}	
							printf("\n");	
						}	
					}									
				}
			}					
				
	}
	//fclose(fp);
	//fclose(fp2);
	
	return 0;
} 

习题3-7

#include<stdio.h>
#include<string.h>
int main()
{
	int k;
	scanf("%d",&k);
	while(k--)
	{ 
		char input[60][1200],res[1200];
		char ch;
		int m,n;
		scanf("%d",&m);
		scanf("%d",&n);
		ch=getchar();
		for(int i=0;i<m;i++)
		{   int j=0;
			while((ch=getchar())!='\n')
			input[i][j++]=ch;
			if(ch=='\n')
			continue;		
		}
		
		for(int i=0;i<n;i++)
		{
			int numA=0,numC=0,numG=0,numT=0;
			for(int j=0;j<m;j++)
			{
				switch(input[j][i])
				{
					case 'A':
					{
						numA++;
						break;
					}
					case 'C':
					{
						numC++;
						break;
					}
					case 'G':
					{
						numG++;
						break;
					}
					case 'T':
					{
						numT++;
						break;
					}
					
					
				}
				
				
			}
			int tmp=numA;
			if(numA>=numC&&numA>=numG&&numA>=numT)
			{
				res[i]='A';
				printf("%c",'A');
			}
				
			if(numC>numA&&numC>=numG&&numC>=numT)
			{
				res[i]='C';
				printf("%c",'C');
				
			}
			if(numG>numA&&numG>numC&&numG>=numT)
			{
				res[i]='G';
				printf("%c",'G');
				
			}
			if(numT>numA&&numT>numC&&numT>numG)
			{
				res[i]='T';
				printf("%c",'T');
				
			}
			if(i==n-1)
			printf("\n");
			
		}
		int Han=0;
			for(int i=0;i<n;i++)
			{
				for(int j=0;j<m;j++)
				{
					if(input[j][i]!=res[i])
					Han++;
				}
				
				
			}
			printf("%d\n",Han);
						
	}	
	return 0;
}
习题3-8

解决本题目一般而言有两个思路:

一是得到小数结果再进行判断,如果这样做相当困难,需要做字符串的匹配,算法时间复杂度很高,并且使用double,也不能存储小数点后组都过的维数。

二是从过程着手,边计算边处理,从中发现,当在做除法的时候,除数不会变化,被除数一直改变。当当前被除数N曾经出现过时,就说明此时是循环节的结束点,而第一次出现的被除数N的地方则为循环节的开始点。

另外此题还是要注意输出格式。

#include<stdio.h>
#include<string.h>
int main()
{
	int a,b;
	//FILE *fin,*fout;
	//fin=fopen("datain.txt","rb");
	//fout=fopen("dataout.txt","wb");
	while(scanf("%d",&a)!=EOF)
	{
		int ans[3000],xhj[3000],bcs[3000];
		int lenth,i=1,index=0;
		memset(bcs,-1,sizeof(bcs));
		memset(bcs,-1,sizeof(ans));
		scanf("%d",&b);
		printf("%d/%d = %d.",a,b,a/b);
		a=a%b;
		while(1)
		{	
			bcs[a]=i++;
			a=a*10;
			ans[index++]=a/b;
			a=a%b;
			if(bcs[a]!=-1){
				break;
			}
		}
		//printf("%d %d",bcs[a],i);
		lenth=i-bcs[a];
		for(int m=0;m<bcs[a]-1;m++)
		{
			printf("%d",ans[m]);	
		}		
		printf("(");
		if(i<50){
			
			for(int m=bcs[a]-1;m<i-1;m++)
			{
			printf("%d",ans[m]);
			}
		}
		else
		{	
				
			for(int m=bcs[a]-1;m<50;m++)
			{
			printf("%d",ans[m]);
			}	
			printf("...");
			
			
		}
		
		printf(")\n");
		printf("   %d = number of digits in repeating cycle\n",lenth);
		printf("\n");		
		
		
	}
	
	//fclose(fin);
	//fclose(fout);
	return 0;
} 

习题3-9

思路:首先将问题转换,要使t中删除0个或者多个字符得到s。

需要满足的条件为:t中有s的字符,并且顺序与s的一样。

在t中按照顺序去找s里面的字符,如果全部找到,则输出Yes,否者输出No。

注意事项:字符数组声明的时候,一定要大的,过小会造成,系统判断为RunTime Error。

#include<stdio.h>
#include<string.h>
int main()
{
	char s[1000000],t[1000000];
	//FILE *fin,*fout;
	//fin=fopen("datain.txt","rb");
	//fout=fopen("dataout.txt","wb");
	while(scanf("%s",s)!=EOF)
	{
		scanf("%s",t);		
		int lenths,lentht,j=0;
		lenths=strlen(s);
		lentht=strlen(t);
		for(int i=0;i<lentht;i++){
			if(t[i]==s[j]){
				j++;
			}
			
		}
		if(lenths==j)
			printf("Yes\n");
		else
			printf("No\n");
		 
		
	}
	//fclose(fin);
//	fclose(fout);
	return 0;
}
习题3-10

思路:长方体是由长、宽、高组成,长方形的两边是长宽高三者中的两者。那么只需要统计长,宽,高的数量就能够判断。

本题的难点在于如果长方体中由两个正方形、四个正方形、六个正方形的时候,需要再次判断。

#include<stdio.h>
#include<string.h>
int main()
{
	int m;
	//FILE *fin,*fout;
	//fin=fopen("datain.txt","rb");
	//fout=fopen("dataout.txt","wb");
	while(scanf("%d",&m)!=EOF)
	{ 	
		int amount[3],ans[3];
		memset(amount,0,sizeof(amount));
		memset(ans,0,sizeof(amount));
	    ans[0]=m;
	    amount[0]++;	
		scanf("%d",&m);
		ans[1]=m;
		amount[1]++;
		int n=10;
		int j=-1;	
		while(n--)
		{
			
			scanf("%d",&m);
			if(n%2==1)
			{
			
					if (ans[0]==m)
					{
						amount[0]++;
						j=0;
					}
						
				    else if (ans[1]==m)
					{
						amount[1]++;
						j=1;
					}
				    	
					else if(ans[2]==m)
					{
						amount[2]++;
						j=2;
					}	
					else if(ans[2]==0)
					{
						ans[2]=m;
						amount[2]++;
						j=2;
					}
					else
					{
						j=-1;
					} 
					
					
			}
			else
			{
				if (ans[0]==m&&j!=0)
					{
						amount[0]++;
						
					}
						
				    else if (ans[1]==m&&j!=1)
					{
						amount[1]++;
						
					}
				    	
					else if(ans[2]==m&&j!=2)
					{
						amount[2]++;
						
					}	
					else if(ans[2]==0)
					{
						ans[2]=m;
						amount[2]++;
					}
						
			}
		
		}
			if(amount[0]==4&&amount[1]==4&&amount[2]==4)
			printf("POSSIBLE\n");
			else if (amount[0]==6&&amount[1]==2&&amount[2]==4)
			printf("POSSIBLE\n");
			else if (amount[0]==4&&amount[1]==6&&amount[2]==2)
			printf("POSSIBLE\n");
			else if (amount[0]==6&&amount[1]==4&&amount[2]==2)
			printf("POSSIBLE\n");
			else if(amount[0]==6&&amount[1]==6)
			printf("POSSIBLE\n");
			else			
			printf("IMPOSSIBLE\n");
		
	}
	//fclose(fin);
	//fclose(fout);
	
	return 0;
}

习题3-11

问题分析:

本题目,可以认为是两个数组进行错位匹配。这样就会出现两种情况:

1、数组1匹配数组2,数组1比对开始点变化,数组2需要全匹配。

2、数组2匹配数字1,数组2比对开始点变化,数组1进行全匹配。

在匹配的过程中,又会出现两种情况:

1、有数组能够匹配,那么长度就为数组1的长度加上数组2的长度减去匹配的数字个数。

2、没有一个数字能够匹配,那么长度就为数组1的长度机上数组2的长度。

其他情况:在实际上,可能会出现这样一种情况。长条1和长条2匹配,长条2可以进行翻转(也就是数组逆序)和长条1进行匹配,得到最小值。本题并没有考虑这种情况。

本题目:如果使用函数,会更加简洁,但是考虑到这是第四章才讲函数,暂时不用。

#include<stdio.h>
#include<string.h>
int main()
{
	//FILE *fin,*fout;
	//fin=fopen("datain.txt","rb");
	//fout=fopen("dataout.txt","wb");
	char m[1000],n[1000],dn[1000];
	while(scanf("%s",m)!=EOF)
	{
		scanf("%s",n);
		int start,flag,lenthm,lenthn;
		start=0;
		flag=1;
		lenthm=strlen(m);
		lenthn=strlen(n);
		for(int i=0;i<lenthn;i++)
		{
			dn[lenthn-i-1]=n[i];
			
		}
			
		int i=0,j=0,p=0;
		int min1,min2,min3,min4;
			while(start<lenthm)
			{	
				p=0;
				flag=1;
				i=start;
				j=0;
				while(j<lenthn&&i<lenthm)
				{	
					int tmpm,tmpn;
					tmpm=int(m[i++])-48;
					tmpn=int(n[j++])-48;
					if(tmpm+tmpn>3)
					{
						flag=0;
						break;
					}
						
					else
						p++;
					
				}
				if(flag==1)
				{
					min1=lenthn+lenthm-p;
					break;
				}
				else
				{
					start++;
				}
				
			}
			if(start==lenthm)
			min1=lenthn+lenthm;			
		
			start=0;
			while(start<lenthn)
			{	
				p=0;
				flag=1;
				j=start;
				i=0;
				while(j<lenthn&&i<lenthm)
				{	
					int tmpm,tmpn;
					tmpm=int(m[i++])-48;
					tmpn=int(n[j++])-48;
					if(tmpm+tmpn>3)
					{
						flag=0;
						break;
					}
						
					else
						p++;
					
				}
				if(flag==1)
				{
					min2=lenthn+lenthm-p;
					break;
				}
				else
				{
					start++;
				}
				
			}
			if(start==lenthn)
			min2=lenthn+lenthm;	
		
			if(min1<min2)
			printf("%d\n",min1);
			else	
			printf("%d\n",min2);		
				
				
	}
	//fclose(fin);
	//fclose(fout);
	return 0;
} 
习题3-12

问题分析:本题本质是一个二进制十进制转换问题。可以从两个角度考虑。

1、先将输入的十进制转为二进制,然后再通过转化的二进制数来求得表示这个二进制需要的尾数个数和阶码个数。

2、将二进制转为十进制,题目已经告诉了表示这个二进制数的尾数个数和阶码个数。

具体实施时候,会发现如果采用角度1进行编码,将遇到以下两个问题:

1、枚举次数太多,时间过长失败。解决方法:暂无。

2、输入溢出,计算溢出。

因为时间太长,导致这个角度的解决方法暂时搁浅,那么角度2来解决这个问题,

目标:计算当M(尾数的个数)为i时,E(阶码的个数)为j的时候,所能够表示的最大(最小)十进制数。

问题:直接转化成十进制数还是会溢出。

解决方案:将这个十进制数分成阶码和尾数分别表示。(可行)

首先求与M(尾数的个数)为i时,E(阶码的个数)为j能表示的最大二进制数相等的10的x次方数。(x为浮点数)。

那么x的整数部分就为M(尾数的个数)为i时,E(阶码的个数)为j时的所能表示的最大十进制数的阶码(即为输入中e后面的数)

10的x小数部分次方就为十进制数的尾数,当x=0时,10的x次数为1,当x=1时,10的x次方为10,那么10的x小数部分次方的范围在1到10.

但是输入是0到10,所以还需要一次转换。

输入值能采用字符输入再转换,以避免溢出。

#include<stdio.h>
#include<math.h>
#include<string.h>
int main()
{
	//FILE *fin,*fout;
	//fin=fopen("datain.txt","rb");
	//fout=fopen("dataout.txt","wb");
	 double M[15][35];
	 long int E[15][35];//m为尾数,E为阶码 
	 
	 for(int i=0;i<=9;i++)
	 {
	 	for (int j=1;j<=30;j++)
		 {
		 	double m,e,tmp;
		 	m=1-pow(2,-1-i);//用第i位表示尾数时,尾数的最大值
			e=pow(2,j)-1; 
		 	tmp=log10(m)+e*log10(2);//通过对数公式,求得10为的底的指数(小数)
			E[i][j]=tmp;//因为E是整形数组,那么自会将tmp的整数部分存入E中。 
			M[i][j]=pow(10,tmp-E[i][j]);
			 
		 }
	 } 
	 char input[100];
	 while(scanf("%s",input)&&!(input[0]=='0'&&input[1]=='e'&&input[2]=='0'))
	 {
	 	int lenthin,ein,e;//定位e的位置
	 	double m10d;
	 	int e10d=0;
		lenthin=strlen(input);		  
	 	for(int i=0;i<lenthin;i++)
		 {
		 	
		 	
		 	if(input[i]=='e')
		 	 		ein=i; 	
		 }
		 m10d=(int(input[0])-48)*pow(10,0);
		 
		 	for(int i=2;i<ein;i++)
		 {
		 	
		 	m10d=m10d+(int(input[i])-48)*pow(10,-i+1); 
		 }
		 
		 for(int i=ein+1;i<lenthin;i++)
		 {
		 	
		 	e10d=e10d+(int(input[i])-48)*pow(10,lenthin-i-1); 
		 }
		 if(m10d<1)
		 	{
		 	e10d=e10d-1;
		 	m10d=m10d*10;
			}
			for (int j=1;j<=30;j++)			
			{
				for(int i=0;i<=9;i++)
			 	{ 
			 		if(e10d<=E[i][j]&&fabs(m10d-M[i][j])<1e-5)
				 	{
				 	printf("%d %d\n",i,j);
				 	break;
				 	}	
			 	}	
			}
		
	 } 	
	//fclose(fin);
	//fclose(fout);
	return 0;
}



                
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值