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

例题4-1

问题提出:已知:两个字符数组,经过两个操作后:1.调换顺序,2.一一映射。问通过这两个操作两个数组是否能一样。

问题分析:如果直接做,先经过随机调换再进行随机映射,显然这样很难解决这个问题,这就需要推导出字符数组1经过这两个操作后变为数组2的充要条件。

当然,想要推导有些困难,我们就只能采用猜想的方式进行猜测这个条件,然后通过理论推导或者大规模实验进行证明。

问题转化:如果字符数字1和字符数组2中字母的个数能够对应起来(比如字符数组1有5个A,2个C。而字符数组中有2个D,5个E,那么5==5,2==2),就能将字符数组1变为字符数组2.

解决方案:1.统计字符数组1和字符数组2中各个字母的个数。

                    2.对个数进行比较。看是否能满足条件。在这里,采用先排序,再比较的方式,降低复杂度。

补充说明:源码中采用了书中的排序函数qsort,实际上这个排序函数并不好,不建议以后使用。

#include
#include
#include
//#define Local 

int cmp(const void *a,const void *b)
{
	return *(int*)a - *(int*)b;//升序排序, 
	//return *int( *)b-*int( *)a;//降序排序。 
	
}
int main()
{ 
	//#ifdef Local
	//	freopen("datain.txt","r",stdin);
	//	freopen("dataout.txt","w",stdout);
	//#endif
	char input1[100],input2[100];
	while(scanf("%s",input1)!=EOF&&input1)
	{
		scanf("%s",input2);
		int alphabet1[26],alphabet2[26];
		int lenthinput1,lenthinput2,flag=1;
		memset(alphabet1,0,sizeof(alphabet1));
		memset(alphabet2,0,sizeof(alphabet2));
		lenthinput1=strlen(input1);
		lenthinput2=strlen(input2);
		for(int i=0;i

例题4-2

问题提出:已知答案和猜测两个字符数组,通过比对猜测数组和答案数组中的字符,判断是否猜对?

问题分析:如果要猜测对,需要满足:

1.猜测数组中有答案数组中的全部字符。

2.猜测数组中在猜对最后一个字符之前,错误的字符小于7.

那么猜测错,必须满足

1.猜测数组在猜对最后一个字符之前,错误字符大于7。

那么没有猜测对,也没有猜测错。就为待定状态

解决方案:两个角度

1.用答案数组比对猜测数组

2.用猜测数组比对答案数组。

本编码采用角度2.

补充说明:1、本题和猜测数组的顺序有关系,不能像习题4-1那样可以先统计再判断。

                    2、UVA的debug数据有问题。不适合测试

时间2014/6/20

#include
#include
int main()
{
	//freopen("datain.txt","r",stdin);	
	//freopen("dataout.txt","w",stdout);
	int n;
	while(scanf("%d",&n)&&n!=-1)
	{
		printf("Round %d\n",n);
		char in1[1000],in2[1000];
		scanf("%s",in1);//答案 
		scanf("%s",in2);//猜测 
		int len1,len2,wrong=0,win=0;
		
		len1=strlen(in1);
		len2=strlen(in2);//猜测和答案比对 
		for(int i=0;i=7)
		{
			printf("You lose.\n");
		}

	}
	
	
	return 0;
}

例题4-3

问题提出:n个站成一圈逆时针编号,A逆时针数k个人,B顺时针数m个人。数完之后,淘汰掉A、B数的人。然后接着A顺时针m,B逆时针n。直到所有人全部被淘汰。

问题分析:不需要问题转换,直接根据题意进行编程。

解决方案:利用一个长度为n的people数组存储现在人员的状态,0表示淘汰,非0表示还在游戏中。那么用Apos来指示当前A的位置,Bpos来指示B的位置。

题目中逆时针数数,实际上就是对people数字由小到大的正向数数,逆时针就是由大到小的逆向数数,那么顺时针或者逆时针数数大于一圈的时候,需要多Apos和Bpos进行复位。知道people数组全为0.结束。

补充说明:

1、从问题提出可以看出,A逆时针数数和B顺时针数数是重读多次出现,那么,可以将其写成函数。会使得代码更加简洁。

2、书中给出了一个left来记录剩余的人数,相对于判断people数组全为0,更节省时间。

3、注意输出格式。使用%3d更加简洁。这一点之前我并不知道。

时间:2017/6/20

#include
int main()
{
	//freopen("datain.txt","r",stdin);
	//freopen("dataout.txt","w",stdout);
	int n,k,m;
	while(scanf("%d%d%d",&n,&k,&m)==3&&n&&k&&m)
	{
		int people[1000];
		for(int i=0;i<=n;i++)
		{
			people[i]=i;
		}
		int Apos=1,Bpos=n,flag=1;//A、B官员的位置
		int a,b;
		while(flag)
		{
			a=k;
			b=m;
			while(a)
			{
				if(Apos>n)
				{
					Apos=Apos-n;
				}
			
				if (people[Apos++]!=0)
				{
					a--;
				}
		 	
			}
			while(b)
			{
				if(Bpos<1)
				{
					Bpos=Bpos+n;	
				}
				if (people[Bpos--]!=0)
				{
					b--;
				}
			}
		Apos=Apos-1;
		Bpos=Bpos+1;
		
		people[Apos]=0;
		people[Bpos]=0;
		if(Apos==Bpos)
		{
			if(Apos<10)
		{
			printf("  ");
		}
		if(Apos>=10&&Apos<100)
		{
			printf(" ");
		}
			printf("%d",Apos);	
		}
		else
		{
			if(Apos<10)
		{
			printf("  ");
		}
		    if(Apos>=10&&Apos<100)
		{
			printf(" ");
		}
		printf("%d",Apos);
		if(Bpos<10)
		{
			printf("  ");
		}
		if(Bpos>=10&&Bpos<100)
		{
			printf(" ");
		}
		printf("%d",Bpos);
				
		}
		int flag1=0;
		for(int i=1;i<=n;i++)
		{
			if(people[i]!=0)
			{
				flag1=1;
				break;
			}
                
		}
		if(!flag1)
			flag=0;
		else
		{
			flag=1;
			printf(",");
		}
			
	
	}
	printf("\n");
		
		
	}
	
	return 0;
}
例题4-4

问题提出:给定一段字符通过一定规则的编码成二进制串,然后给出二进制串进行解码。

问题分析:本题中包含两个过程,即编码和解码。完成这两个功能就可。

根据编码规则需要建立二进制串和字符的映射。首先我们可以采用key-value这样的表示方法的数据结构(比如C#中的list),但是在这个阶段,没有提供这样的数据结构。那么我们需要对存储做一下转换。我们可以发现,此时的编码和编码长度有关。并且编码长度决定在这个编码长度下能够编码的个数。比如编码长度为1,那么可以编码1个。如果为2,则为3个,如果为i,就为2^i-1。那么我们就可以将编码存储在一个(编码长度,序号)的二维数组中。

解码方面,就是一个字符读入和二维数组匹配过程。

解决方案:

1、通过code[编码长度][序号]存储编码。

2、边读边解码。

补充说明:

1、本题目是字符处理类的题目,其实核心编程思想不难,主要是处理输入输出有些麻烦,所以一定要一步一步的做,不秀技巧,尽量朴实好调试。

2、本题目中解码输入可以换行,导致如果直接用getchar()会接收到回车,如果在主函数中使用多个getchar()去接收回车以抵消无效输出,会导致调试非常麻烦,强烈建议写一个不读取回车的字符读入函数,源代码中自定义getnonchar()函数来完成这个功能。

时间:2017/6/21

#include
#include
#include
char getnonchar()
{
	char c;
	c=getchar();
	while(c=='\n'||c=='\r')
	{
		c=getchar();
	}
	return c;
}
int read01(char code[8][256],int ws)
{
	int ed,code01=0;
	char c;
	int a=0;
	ed=pow(2,ws)-1;
	do
	{
	if(a!=0)
	{
		printf("%c",code[ws][code01]);
	}
	code01=0;
	for(int i=0;i=pow(2,i)-1)
			{
				i++;
				j=0;
			}
			code[i][j++]=c;
		}
		//解码
		char a1,a2,a3;
		int  ws;
		a1=getnonchar();
		a2=getnonchar();
		a3=getnonchar();
		 while(!(a1=='0'&&a2=='0'&&a3=='0'))
		 {
		 	ws=(a1-'0')*pow(2,2)+(a2-'0')*pow(2,1)+(a3-'0')*pow(2,0);
		 	read01(code,ws);
		 	a1=getnonchar();
			a2=getnonchar();
			a3=getnonchar(); 	
		 }
		 printf("\n");
	}
	
	return 0;
} 

例题4-5

本题目书中进行了详细分析,尤其第二种的思维模式,值得借鉴,非常有用,现贴出第二种思维模式的代码。

时间:2017/6/23

#include
#include
#define maxd 10000
struct Command
{
	char c[5];
	int r1,c1,r2,c2;
	int a,x[20];
}cmd[maxd];
int r,c,n;
int simulate(int* r0,int* c0)
{
	for(int i=0;i0)
			printf("\n");
		printf("Spreadsheet #%d\n",++kase);
		
		scanf("%d",&q);
		while(q--)
		{
			scanf("%d%d",&r0,&c0);
			printf("Cell data in (%d,%d) ",r0,c0);
			if(!simulate(&r0,&c0))printf("GONE\n");
			else printf("moved to (%d,%d)\n",r0,c0);
		}
	}
	
	return 0;
}

例题4-6

每个功能写成一个函数。调试的时候,先每个函数进行测试,再总体测试。

#include 
#include 
char SID[300][15];
int CID[300];
char name[300][15];
int score[300][4];
int cpos=0;
void Showmainmenu()
{
	printf("Welcome to Student Performance Management System (SPMS).\n\n");
	printf("1 - Add\n");
	printf("2 - Remove\n");
	printf("3 - Query\n");
	printf("4 - Show ranking\n");
	printf("5 - Show Statistics\n");
	printf("0 - Exit\n\n");
}
int  QuerySID(char cSID[15])
{
	for(int i=0;i=score[j][0]+score[j][1]+score[j][2]+score[j][3]&&i!=j)
						rank--;
				}
				    printf("%d ",rank);
				    printf("%s %d %s %d %d %d %d ",SID[i],CID[i],name[i],score[i][0],score[i][1],score[i][2],score[i][3]);
					printf("%d %.2f\n",totalscore,(float(totalscore)+1e-5)/4);
					
						
			}		 		
		}
		printf("Please enter SID or name. Enter 0 to finish.\n");		
	}
		
			
}
void Showranking()
{
	
	printf("Showing the ranklist hurts students' self-esteem. Don't do that.\n");
	
}

void  Showstatistics()
{
	
	printf("Please enter class ID, 0 for the whole statistics.\n");
	int k,pass[4],passp[300][4],passp2[5],totalscore[4];
	memset(pass,0,sizeof(pass));
	memset(passp,0,sizeof(passp));
	memset(totalscore,0,sizeof(totalscore));
	memset(passp2,0,sizeof(passp2));
	scanf("%d",&k);
	if(k!=0)
	{
		int sump=0;
		for(int i=0;i=60)
					{
						pass[j]++;
						passp[i][j]++;
						
					}
				}
				
			}	
		}
		
		for(int i=0;i<4;i++)
		{
			if(i==0)
			printf("Chinese\n");
			if(i==1)
			printf("Mathematics\n");
			if(i==2)
			printf("English\n");
			if(i==3)
			printf("Programming\n");
			if(sump==0)
			{
				printf("Average Score: %s\n","-nan");	
			}
			if(sump!=0)
			{
			printf("Average Score: %.2f\n",(double(totalscore[i])+1e-5)/sump);				
			}
			printf("Number of passed students: %d\n",pass[i]);
			printf("Number of failed students: %d\n\n",sump-pass[i]);
		}
			printf("Overall:\n");
			for(int i=0;i=60)
					{
						pass[j]++;
						passp[i][j]++;
						
					}
				}	
		}
		
		for(int i=0;i<4;i++)
		{
			if(i==0)
			printf("Chinese\n");
			if(i==1)
			printf("Mathematics\n");
			if(i==2)
			printf("English\n");
			if(i==3)
			printf("Programming\n");
			if(sump==0)
			{
				printf("Average Score: %s\n","-nan");	
			}
			if(sump!=0)
			{
			printf("Average Score: %.2f\n",(double(totalscore[i])+1e-5)/sump);				
			}
			printf("Number of passed students: %d\n",pass[i]);
			printf("Number of failed students: %d\n\n",sump-pass[i]);
		}
			printf("Overall:\n");
			for(int i=0;i

习题4-1(WA,并不知道问题出在什么地方)

本题思路:黑棋的将走法,有5种。分别为:1.初始时,红方的将和黑方的将相对,不能将死;2.黑将向左一步的位置,被红方将死(即红方的棋能够到达那个位置);3.黑将的右边一步。4.黑将的上边一步;5。黑将的下边一步。

那么只要保证这5种走位,都被红方封杀,就能保证将死的状态。

#include
#define maxp 50
struct pieces
{
	char type;
	int x;
	int y;	
}ps[maxp];
int lenps;
int BGx,BGy,RGx,RGy;
int Gcheck(int x1,int y1,int x2,int y2)
{
	int p=0;//
	for(int i=0;ix2&&ps[i].xy1&&ps[i].yy2&&ps[i].yx1&&ps[i].xx2&&ps[i].xy1&&ps[i].yy2&&ps[i].yx1&&ps[i].xx2&&ps[i].xBGx&&ps[i].x1)
		{
			down=canArrive(BGx-1,BGy);
		}
		else
		{
			down=0;//1说明不能将死,0说明能够将死 
		}
		if(BGx<3)
		{
			up=canArrive(BGx+1,BGy);
		}
		else
		{
			up=0;
		}
		if(BGy>4)
		{
			left=canArrive(BGx,BGy-1);
		}
		else
		{
			left=0;
		}
		if(BGy<6)
		{
			right=canArrive(BGx,BGy+1);
		}
		else
		{
			right=0;
		}
		if(!up&&!down&&!right&&!left&&!flag)
		{
			printf("YES\n");
		}
		else
		{
			printf("NO\n");
		}
			
	}
	

return 0;	
}

习题4-2

本题思路:根据输出形式,可以先确定大小,再一个点一个点的搜索,来确定满足条件的正方形个数。再改变大小,进行搜索。

#include
#include
#define maxn 1000
int Hori[maxn];
int Vert[maxn];
int Findsquare(int n,int sp,int size)
{
	
	for(int i=0;i

习题4-3

习题4-4

本题思路:模拟骰子的x、y、z轴旋转,穷举出所有情况,然后和初始的骰子进行匹配,看是否一样。本题应该有更加简单的搜索方法,还需要从后面章节进行学习。

#include
#include
int isequal(char cube1[7],char cube2[7])
{
	int flag=1;
	for (int i=1;i<7;i++)
	{
		if(cube1[i]!=cube2[i])
			flag=0;
	}
	if(flag)
	{
		return 1;
	}
	
	else
	{
		return 0;
	}
	
}

int horirotation(char *cube2)
{
	char tmp;
	tmp=cube2[2];
	cube2[2]=cube2[4];
	cube2[4]=cube2[5];
	cube2[5]=cube2[3];
	cube2[3]=tmp;
	
}
int verirotation(char *cube2)
{
	char tmp;
	tmp=cube2[2];
	cube2[2]=cube2[1];
	cube2[1]=cube2[5];
	cube2[5]=cube2[6];
	cube2[6]=tmp;	
}

int lrrotation(char *cube2)
{
	char tmp;
	tmp=cube2[1];
	cube2[1]=cube2[4];
	cube2[4]=cube2[6];
	cube2[6]=cube2[3];
	cube2[3]=tmp;	
	
}

int main()
{
	//freopen("datain.txt","r",stdin);
	//freopen("dataout.txt","w",stdout);
	char cube1[7],cube2[7],cube3[7],cube4[7];
	char m;
	
	while(scanf("%c",&m)!=EOF)
	{
		cube1[1]=m;
		int flag=0;
		for(int i=2;i<7;i++)
		{
			scanf("%c",&m);
			cube1[i]=m;	
		}
		for(int i=1;i<7;i++)
		{
			scanf("%c",&m);
			cube2[i]=m;	
			cube3[i]=m;
		}
		//判断是否相同 
		
		int hori=4,veri=4,lr=4;
		while(veri--)
		{
			while(hori--)
			{
				for(int i=1;i<7;i++)
				{
					cube4[i]=cube3[i];//保存水平状态 
				}
				while(lr--)
				{	
					lrrotation(cube3);
					if(isequal(cube1,cube3))
					{	
						flag=1;	
					}	
				}				
				for(int i=1;i<7;i++)
				{
					cube3[i]=cube4[i];
				}
				horirotation(cube3);
				if(isequal(cube1,cube3))
				{
					flag=1;	
				}
				lr=4;	
				
			}
			for(int i=1;i<7;i++)
			{
				cube3[i]=cube2[i];
			}
			verirotation(cube3);
			if(isequal(cube1,cube3))
			{
				flag=1;	
			}
			for(int i=1;i<7;i++)
			{
				cube2[i]=cube3[i];//保存垂直状态 
			}
			hori=4;
		}
	
	if(flag)
	{
		printf("TRUE\n");
		
	}
	else
	{
		printf("FALSE\n");
	}
			
	scanf("%c",&m);	
	}
	
	
} 

习题4-8

本题思路:通过模拟每个时刻的状态,来判断。本程序模拟1000次,在1000次中寻找全醒的时刻,如果有,则输出,没有则输出-1。

#include
#define N 100
 typedef struct 
{
	int wk[20];
	int lenthwk; 
	int p;
}Stu;
int main()
{
	//freopen("datain.txt","r",stdin);
	//freopen("dataout.txt","w",stdout);
	int m,rnd=1;
	while(scanf("%d",&m)&&m)
	{
		Stu stu[N];
		int find=0;
		for(int i=1;i<=m;i++)
		{
			int wake=0,sleep=0,ini=1;
			scanf("%d",&wake);
			for(int j=0;j0&&j==wake)
				{
					stu[i].wk[wake]=2;
				}
				else
				{
					stu[i].wk[j]=1;	
				}	
			}
			scanf("%d",&ini);
			stu[i].p=ini-1;
			stu[i].lenthwk=wake+sleep;		
		}
		int amslee=0,amwake=0;
		for(int i=1;i<=m;i++)
		{
			if(stu[i].wk[stu[i].p]==2||stu[i].wk[stu[i].p]==1)
			   	amslee++;
			else
				amwake++;	
		}		
		for(int i=1;i<=500;i++)
		{
			if(amwake==m)
			{
				find=i;
				break;
			}
			else
			{
				for(int j=1;j<=m;j++)
				{
					int tmp=(stu[j].p+1)%(stu[j].lenthwk);
					if(stu[j].wk[tmp]!=2)
					{
						stu[j].p=tmp;
					}
					else
					{
						if(amslee>amwake)
						{
							stu[j].p=tmp;	
						}
						else
						{
							stu[j].p=0;
						}
	
					}
				
				}	
			}
			amslee=0;amwake=0;
			for(int i=1;i<=m;i++)
			{
			if(stu[i].wk[stu[i].p]==2||stu[i].wk[stu[i].p]==1)
			   	amslee++;
			else
				amwake++;
			}				
		} 
		if(find)
			printf("Case %d: %d\n",rnd++,find);
		else
			printf("Case %d: -1\n", rnd++);
	
	}

	return 0;
} 

习题4-10

本题思路:首先将水的立方数换算为高度,将每个格子的高度进行排序,再一个一个填满,填满的时候,高度逐步减少,直至为0或者为负数的时候停止。因为换算高度的时候要除以100。所以会出现float和double类型不准的情况,这个时候就可以使用书中p95的方法,加上一个很小的数EPS,来校准精度。

#include
#include
#define EPS 1e-4
using namespace std;
float dw(float water,int a,int amount) {
	return water-a*amount;
}
int main() {
	//freopen("datain.txt","r",stdin);
	//freopen("dataout.txt","w",stdout);
	int m,n,rnd=1;
	while(scanf("%d",&m)&&m) {
		int reg[1000],index=-1;
		double water,watertmp,wl,per;
		scanf("%d",&n);
		for(int i=0; i0) 
			{
				watertmp=water;
			} 
			else 
			{
				index=i;
				break;
			}
		}
		if(index==-1) {
			index=m*n-1;
		}
		wl=watertmp/(index+1);
		per=double((index+1)*100)/(m*n);
		printf("Region %d\n",rnd++);
		printf("Water level is %.2lf meters.\n",wl+reg[index]+EPS);
		printf("%.2lf percent of the region is under water.\n\n",per+EPS);



	}




	return 0;
}





你可能感兴趣的:(算法竞赛)