演算法競賽入門經典(第2版)-劉汝佳-第三章解題原始碼(C語言)

HGaviN發表於2017-06-07

習題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;
}



相關文章