第三次机考总结

总结一下这次机考(可略过):

本人真是越考越没水平了,明明之前还发博客认真研究过的,转眼,就写不对了。。比如
A.递归求解之阿尔法乘积,之前就遇到过这样的坑,设置传值的变量类型和接受传值变量的类型不一样导致的第九组数据WA。
B. 贴邮票三,又是贴邮票,这次,我却没过。。不理解题意,不知道要怎么做。不对,我思路是对的,只是不想去debug(捂脸)
C. 打印方阵一,和蛇形矩阵差不多的题,只要找到限制条件就可。受蛇形矩阵影响太深,直接把条件搬上去,就错了
D. 大牌点一,如果仔细想的话,思路是很好想的,但是,今天没有仔细想,直接就写了,既浪费了时间又过不了题。
E. 最大的最小值:我感觉和求矩阵的鞍点差不多,但,没时间了。。
(我决定改名了,叫写程序先想后写)
在这里插入图片描述

A. 递归求解之阿尔法乘积(注意long long类型的坑)

运行时间限制: 1000 运行内存限制: 65536
作者: scshuanghai 是否specialjudge: False
题目描述
计算一个整数的阿尔法乘积。对于一个整数x来说,它的阿尔法乘积是这样来计算的:如果x是一个个位数,那么它的阿尔法乘积就是它本身;否则的话,x的阿 尔法乘积就等于它的各位非0的数字相乘所得到的那个整数的阿尔法乘积。例如:4018224312的阿尔法乘积等于8,它是按照以下的步骤来计算的:
  4018224312 → 418224312 → 3072 → 372 → 42 → 4*2 → 8
编写一个程序,输入一个正整数(该整数可以用long long 存储),输出它的阿尔法乘积。
输入格式:输入只有一行,即一个正整数。
输出格式:输出相应的阿尔法乘积。

注意:此题要求递归求解,如果出现非递归提交,无论是否通过,不论其他提交是否用了递归求解,此题均为0分。

输入样例
4018224312
输出样例
8

#include<stdio.h>
//#include<>
long long digui(long long n)//!!!long long 类型
{
	if(n/10==0)return n;
	else
	{
		long long m=1;//!!!m与n类型相同
	long long t=n;
	while(t!=0)
	{
		if(t%10!=0)
		m*=(t%10);
		t/=10;
	}
	
	return digui(m);
	}
	
}
int main()
{
	long long n;
	scanf("%lld",&n);
	printf("%lld\n",digui(n));
	return 0;
}

C. 打印方阵一

运行时间限制: 1000 运行内存限制: 65536
作者: scshuanghai 是否specialjudge: False
题目描述
根据给定阶数,输出方阵。具体格式见测试用例。

输入:
为一个整数n(0<n<100),代表方阵阶数。

输出:
一个n*n阶方阵,具体格式见测试用例(注意,每个数之间用一个空格分隔,每行的最后无空格)。

输入样例
4
输出样例
1 4 9 16
2 3 8 15
5 6 7 14
10 11 12 13

以下是AC代码

#include<stdio.h>
int main()
{
	int n,i=0,j=0,path=1,a[101][101]={0},k,l=0;
	scanf("%d",&n);
	a[0][0]=1;
	while(i!=0||j!=n-1)//机考时写错点1
	{
		if(path==1)
		{
			a[i+1][j]=a[i][j]+1;
			i++;
			path=2;
			//printf("1_%d\n",a[i][j]);
			//printf("1_%d\n",a[i][j]);
		}
		else if(path==2)
		{
			a[i][j+1]=a[i][j]+1;
			
			//l++;
			j++;
			//printf("2_%d\n",a[i][j]);
			//printf("l=%d\n",l);
			/*if(l==n-1)
			{
				path=3;l=0;
			}*/
			//printf("2_%d\n",a[i][j]);
			if(a[i-1][j]==0)//如果上一行未被填满,就向上走//机考时写错点2
			path=3;
		}
		else if(path==3)//
		{
			a[i-1][j]=a[i][j]+1;
			i--;
			//l++;
			//printf("3_%d\n",a[i][j]);
			/*if(l==n-1)
			{
				path=4;l=0;
			}*/
			if(i==0&&j!=n-1)//写错点3
			path=4;
		}
		else if(path==4)
		{
				a[j+1][0]=a[i][j]+1; 
				i=j+1;j=0;
				path=2;
				//printf("4_%d\n",a[i][j]);
		}
		//printf("%d\n",path);
	}
	for(i=0;i<n;i++)
	for(j=0;j<n;j++)
	{
		printf("%d ",a[i][j]);
		if(j==n-1)printf("\n");
	}
	return 0;
}

B. 贴邮票三

运行时间限制: 1000 运行内存限制: 65536
作者: scshuanghai 是否specialjudge: False
题目描述
你寄一个快递,又要贴邮票。由于快递费比较贵,可能要贴很多张邮票。你嫌麻烦,所以决定贴尽量少(指张数少)的邮票。

输入:
第一行为两个用空格分隔的整数m和n(0<m,0<n<100),依次代表邮资的额(以分为单位),和你手里邮票的总张数。
第二行为n个用空格分隔的整数,代表分别你手中每张邮票的面值(以分为单位)。

输出:
为一个整数,代表你至少要贴多少张邮票(允许贴上的邮票的总面值超过邮资,但不能不够)。
测试用例保证1、输入合法;2、所有整数都可以用int存储;3、你手中的邮票总面值大于等于邮资。

输入样例
100 5
10 20 30 40 50
输出样例
3
思路:先将邮票按升序排序,然后用m依次减,直到m减不动,m<a[i]时,总数加一,m==0时,总数不变,输出总数
//第二种思路
写的过程还是遇到了问题,以下是AC代码

#include<stdio.h>
#include<time.h>
int main()
{
	int m,n,a[101],i,j,t,total=0;
	scanf("%d%d",&m,&n);
	//srand(time(NULL));m=1+rand()%200;n=1+rand()%20;
	//printf("m_%d,n_%d\n",m,n);
	for(i=0;i<n;i++)
	{
		scanf("%d",&a[i]);
		//a[i]=1+rand()%100;printf("a[%d]=%d ",i,a[i]);
	}
	for(i=0;i<n;i++)
	for(j=0;j<n-i-1;j++)//冒泡排序 
	{
		if(a[j]>a[j+1])
		{
			t=a[j];
			a[j]=a[j+1];
			a[j+1]=t;
		}
	}
	i=n-1;
	while(m!=0&&i>=0)//求最小值 //可能是这个条件的问题,它取不到a[0] //加上=变成i>=0;
	{
		if(m>=a[i])
		{
			m-=a[i];
			total++;
			//i--;放在这个位置会出错,原因,我也没想通,因为执行了if语句之后,else if就不会被执行,而执行了else if中的条件后,下一次不会循环,所以说,i在哪里减不是一样的吗?
			//printf("1_%d m=%d\n",total,m);
		}
		else if(m<a[i])//不管怎样都是total++,结束程序 
		{
			total++;m=0;
		} 
		//printf("2_%d\n",total);
		i--;
	}
	printf("%d\n",total);//判断输出total的条件 //因为测试数据的保证,本题一定有total,无需判断
	return 0;
}

D. 大牌点一

运行时间限制: 1000 运行内存限制: 65536
作者: scshuanghai 是否specialjudge: False
题目描述
在桥牌中大牌点的英文全称是High Card Point,简写为hcp 。它是衡量一手牌好坏的标准。我们规定A=4hcp K=3hcp Q=2hcp J=1hcp.由此,拿到一手牌我们可以很快地计算出他的hcp。我们也可以很快知道,一副牌一共是40Hcp。这个值对于每副牌来说,就是东南西北4个人手上大牌点的总和。

现我们这里有另一种扑克的玩法,其计算牌点的方式如下,请你写一段程序来衡量一下一手排的好坏。
牌点计算方式:3,4,5均为1点,6,7,8均为2点,9,T(代表10)均为3点,J为4点,Q为5点,K为6点,A为8点,2为10点,B(代表小王)为15点,R(代表大王)为20点。

输入:
第一行为一个整数n(0<n<100),共n手牌;
后边为n行,每行为一个长度不超过20的字符串,代表一手牌(字符串中只可能包含字符3456789TJQKA2BR)。

输入:
共n行,将输入的n手牌按牌的点数依次从大到小输出(测试用例保证没有点数相同的牌)。

输入样例
5
QQQ56AJR9Q
T5RJJJT82
RKT8J6QT5A
5T9RJ9J6B
9994A7QRBQ
输出样例
9994A7QRBQ
QQQ56AJR9Q
5T9RJ9J6B
RKT8J6QT5A
T5RJJJT82
思路:先计算每行字符串的值,储存在一个数组中,将它们按值的大小排出的下标顺序赋给另一个数组。
一开始想的时候觉得可以用选择排序,后来,选择排序坑了我那么长时间,还有,一开始计算值的时候遗漏了一些字母

#include<stdio.h>
#include<time.h>
int main()
{
	int n,i,x[101]={0},xu[101],max=0,t,temp,j,flag[101]={0};
	char str[101][21];
	scanf("%d",&n);
	getchar();
	for(i=0;i<n;i++)
	gets(str[i]);
	for(i=0;i<n;i++)
	for(j=0;str[i][j]!='\0';j++)//个人觉得这里用if比switch节省时间,之前还想把它们用数组存起来,但想着查找也麻烦
	{
		if(str[i][j]=='3'||str[i][j]=='4'||str[i][j]=='5')x[i]+=1;
		if(str[i][j]=='6'||str[i][j]=='7'||str[i][j]=='8')x[i]+=2;
		if(str[i][j]=='9'||str[i][j]=='T')x[i]+=3;
		if(str[i][j]=='J')x[i]+=4;
		if(str[i][j]=='Q')x[i]+=5;
		if(str[i][j]=='A')x[i]+=8;
		if(str[i][j]=='K')x[i]+=6;
		if(str[i][j]=='2')x[i]+=10;
		if(str[i][j]=='B')x[i]+=15;
		if(str[i][j]=='R')x[i]+=20;
	}
	/*for(i=0;i<n;i++)
	printf("x_%d\n",x[i]);*/
	for(i=0;i<n;i++)
	{
		for(j=0;j<n;j++)
		{
			if(max<x[j]&&flag[j]==0)//flag用于隔绝之前选出来的最大值
			{
				max=x[j];
				t=j;
			}
		}
		//printf("%d\n",t);
		flag[t]=1;
		xu[i]=t;
		/*temp=x[i];
		x[i]=max;
		x[t]=temp;*/ //交换改变了序号 //选排造的孽
		max=0;
	}
	/*for(i=0;i<n;i++)
	printf("xu_%d\n",xu[i]);*/
	//按照xu的顺序输出
	for(i=0;i<n;i++)
	puts(str[xu[i]]); 
	return 0;
}

这题似曾相识,包括使用flag来隔绝最大值这一方法,之前写题的时候好像遇见过。。
分支结构可以换成数组
以下是老师的代码,使用函数与数组增强了可读性。

#include<stdio.h>
#include<string.h>

//计算一手牌点数 
int		getPoint(char hand[]) ;

//根据点数降序排列  
void	sortS(char poker[][32],int size) ;

int main()
{
	char	poker[110][32];
	int		n , i ;
	
	scanf("%d",&n) ;
	for(i=0;i<n;i++) scanf("%s",poker[i]); //读入字符串 
	sortS(poker,n) ;
	for(i=0;i<n;i++) printf("%s\n",poker[i]); //输出 
	return 0;
}


//计算一手牌点数 
int getPoint(char hand[])
{
	//保存各个牌的点数值 
	int	points[]={0,0,10,1,1,1,2,2,2,3,8,15,0,0,0,0,0,0,0,4,6,0,0,0,0,0,5,20,0,3} ;//先按1到9,再按大写字母顺序来排
	int	i, sum = 0 ;
	
	for( i = 0 ; hand[i] ; i++)
	{
		if ( '0' <= hand[i] && hand[i] <= '9') sum += points[hand[i]-'0'] ;
		if ( 'A' <= hand[i] && hand[i] <= 'T') sum += points[hand[i]-55] ;//A的ascii码值为65,hand里A之前还有10个数,0-9
	}
	return sum ;
}


//根据点数降序排列 
void	sortS(char poker[][32],int size)
{
	int i , pass;
	char temp[32] ;
	
 	for( pass = 1 ; pass < size ; pass++ )
	 	for( i = 0 ; i < size - pass ; i++ ) 
	 	    if( getPoint(poker[i]) < getPoint(poker[i+1]) )
			{ /*交换*/
				strcpy(temp,poker[i]);
				strcpy(poker[i],poker[i+1]);
				strcpy(poker[i+1],temp);
	        }
}

E. 最大的最小值(完全懵逼,没有思路)

运行时间限制: 1000 运行内存限制: 65536
作者: scshuanghai 是否specialjudge: False
题目描述
一个正整数序列共有N个数,现将它们依次划分成M份。划分的要求是序列原有的顺序不变,每份中的所有的数位置要连续。请你写一段程序计算一下这M份中和最大的那一份最小是多少?

样例解释:这5个划分分别为,(10,40)、(30,10)、(50)、(11)、(40)。这种情况下最大的和为50,其他方式最大的那一份的和均大于50。

输入:
第一行为用空格分隔的2个整数,分别代表N和M。(1 <= N <= 100000 ,1 <= M <= N),接下来N行,每行为一个整数,(这些整数均大于等于1且小于等于10000)。

输出:
只有一行,为一个整数,代表此条件下所求得的最小值。

提示:二分查找

输入样例
7 5
10
40
30
10
50
11
40
输出样例
50

说说这题的思路,界定一个解空间的范围,在这个空间里查找满足条件的值。
如何界定?
根据数据范围,N最大为100000个数,这些数最大为10000,故,和的最大值不会超过10000000010,特意留了10个空间防止意外情况发生。
如何查找?
提示为二分查找
去找了一下查找方法,基于我现有的知识,能用的有顺序查找和二分查找,顺序查找就是循环,一个个比对。
如何判断是否满足条件?
通过二分查找界定一个分份数的值,如果下一个数字加进来会超过这个值,那就不加这个数字,并且完成一份的打包。继续进行下一份。反之,若不超过,那就加进来,继续找这份的其他元素。最后与m值对比。若相等则该值满足,修改上界,寻找最优解;不相等,则修改下界,继续找。

附上老师的代码

#include <stdio.h>

#define		MAXN	100010
#define		MAX		1000000010

/****************************************************************** 
功能:	判断某个值是否满足要求
参数:	containers :存放数列的数组 
		int n :数列的长度 
		int w :待判断的值 
		int m :划分的份数
返回值:满足要求则返回1,否则返回0 。
		所谓满足要求就是如果每份最大是w,是否能完成划分 
*******************************************************************/ 
int judge(int containers[],int n,int w,int m) ;

/****************************************************************** 
功能:	二分搜最大的最小值 
参数:	containers :存放数列的数组 
		int n :数列的长度 
		int m :划分的份数
返回值:最大的最小值  
*******************************************************************/ 
int binarySearch(int containers[],int n,int m) ;

int main()
{
	int		i, n, m, left, right, middle ; 
	int		containers[MAXN] ;
	
    scanf("%d%d",&n,&m); 
	for( i = 0 ; i < n ; i++ )   scanf("%d",&containers[i]);   
	printf("%d\n", binarySearch(containers,n,m)) ;
    
    return 0;
}

/****************************************************************** 
功能:	判断某个值是否满足要求
参数:	containers :存放数列的数组 
		int n :数列的长度 
		int w :待判断的值 
		int m :划分的份数
返回值:满足要求则返回1,否则返回0 。
		所谓满足要求就是如果每份最大是w,是否能完成划分 
*******************************************************************/ 
int judge(int containers[],int n,int w,int m)//依据middle为基线的分法来判断是否要改变上界 
{
	int		isOk = 1 , i,  remainder = 0 ;//余数,如果余数超过下一个元素,说明将该数字加进去不会超过界定的最大值
	//如果余数未超过下一个元素,说明加进去会超过界定的最大值,这样就完成了一份的分割 
	
	for( i = 0 ; i < n && isOk ; i++ )
	{
		if ( w < containers[i] ) isOk = 0 ;//所求值应大于数组内所有元素 
		if ( remainder < containers[i] )  //如果余数小于现在的元素,分完一份 
		{
			m-- ; 
			if ( m < 0 ) isOk = 0 ;//如果减完后m<0说明,该分法不满足,mid取值过小 ,若m>0,说明,mid大于了所求最优解,导致份数分的少 
			remainder = w - containers[i] ;  //重新赋值余数,讨论下一份的分法 
		}
		else remainder -= containers[i] ;	//分到这一份 
		printf("remainder=%d\n",remainder);
	}
	printf("w=%d,isOK=%d\n",w,isOk);
	return isOk ;
}

附上自己的问题代码,问题在于,使用统计份数的多少与m比较,边界改变的条件没限定好。

#include<stdio.h>
#define MAX 1000000010
#define MAXN 100001
int HalfSearch(int xulie[],int n,int m);
int Judge(int mid,int xulie[],int n);
int main()
{
	int xulie[MAXN],n,m,cmp,i;
	scanf("%d%d",&n,&m);
	for(i=0;i<n;i++)
	{
		scanf("%d",&xulie[i]);
	}
	//HalfSearch(xulie,n,m);
	printf("%d\n",HalfSearch(xulie,n,m));
	return 0;
} 
int HalfSearch(int xulie[],int n,int m)
{
	int left=1,right=MAX,mid;
	while(left<right)
	{
		mid=(left+right)/2;
		if(Judge(mid,xulie,n)<m)right=mid;
		else if((Judge(mid,xulie,n)>1)) left=mid+1;//问题在这个地方,找不好移动的边界
	}
	return right;
}
int Judge(int mid,int xulie[],int n)
{
	int isOK=1,remainder=0,i,sum=-1;
	for(i=0;i<n&&isOK==1;i++)
	{
		if(mid<xulie[i])isOK=0;
		else 
		{
			if(remainder<=xulie[i])
			{
				sum++;
				remainder=mid-xulie[i];
			}
			else remainder-=xulie[i];
		}
		
		printf("mid=%d, remainder=%d, sum1=%d\n",mid,remainder,sum);
	}
	
	printf("sum=%d\n",sum);
	return sum;
}

部分结果如下图,60-30那个部分明显边界移动出错
在这里插入图片描述
明天再研究改法
同类型的题NOI / 1.11编程基础之二分查找

//(我希望这会是命运的转折)

评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值