杭电复试2018笔试题目解答

题目1:杭电实验室会定期去电影院看电影,按照惯例,每个成员需要先抽一个号码。
给出n个人的名字,各抽取一个数字, 自己用一种数据结构存取人的名字和抽取数字信息(票数)
例如:BOB 9 Alice 12 Tom 5 jack 7 Nick 4…
1.定义一种数叫丑数,其因子除1外只有2.3.5的倍数,(例如4,10,是丑数,11,13不是),输出所有抽到丑数的人的名字
2. 根据个人所抽数字大小升序排序, 输出排序后的所有名字
3.现有一个新名字加入,将名字插入所有名字中间(n/2)处,并排序输出所有名字
5
BOB 9 Alice 12 Tom 5 jack 7 Nick 4
*
//题目中有插入操作,可以选择链表,但是又有排序操作,只有直接插入排序可以对链表进行处理
//所以选用静态链表 动态链表之所以是动态的,是因为需要指针去建立联系

#include<cstdio>
#include<algorithm>
using namespace std; 
const int maxn=100;
struct node{
	char id[10];
	int num,adress;
	int next;//指针域 
	bool flag;//表示该数是丑数,即在链表上 
//	int order;//记录有效节点在链表上的序号 
}stu[maxn];

bool cmp(node a,node b){
	if(a.flag!=b.flag) return a.flag>b.flag;
	else return a.num<b.num;
} 

bool isugly(int num){
	while(num%2==0){
		num=num/2;
	}
	while(num%3==0){
		num=num/3;
	}
	while(num%5==0){
		num=num/5;
	}
	//去除完所有2,3,5的因子之后 
	if(num==1) return true;
	else return false;
}

int main(){
	for(int i=0;i<maxn;i++){
		stu[i].flag=false;
	} 
	int n,data;
	int count=0;//记录丑数个数 
	char str[20];
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		scanf("%s %d",stu[i].id,&stu[i].num);
		stu[i].adress=i;
		if(isugly(stu[i].num)){//是丑数的话将其标注 
			stu[i].flag=true;
			count++; 
			printf("%s ",stu[i].id);//输出所有抽到丑数的人的名字 
		} 
	} 
	printf("\n"); 
	sort(stu,stu+maxn,cmp);
	printf("排序后的所有是丑数的名字:\n");
	for(int i=0;i<count;i++){
		printf("%s ",stu[i].id);//排序后输出所有是丑数的人的名字
		if(i!=count-1){
			stu[i].next=i+1;
		}else{
			stu[i].next=-1;	//最后一个结点的指针域设为-1 
		}	 
	} 
	printf("\n");
	//插入一个名字结束
	char name[10];
	printf("Please input a name:\n");
	scanf("%s",name); 
	for(int i=0;i<count/2;i++){
			printf("%s ",stu[i].id);//输出前一半所有人的名字
	}
	printf("%s ",name);
	for(int i=count/2;i<count;i++){
			printf("%s ",stu[i].id);//输出前一半所有人的名字
	}
	/*这个是错误的,因为连接地址自己设的不对 ,只有插入一个名字的时候,才是对的, 
	//可以添加多个名字都是在中间,新来的放在末尾 
	while(printf("Please input a name,end表示结束:\n"),scanf("%s",stu[count].id),stu[count].id!="end"){
		stu[count].adress=count;
		stu[count/2-1].next=count;
		stu[count].next=count/2;
		count++;//统计所有人数 
		for(int p=0;p!=-1;p=stu[p].next){
			printf("%s ",stu[p].id);//输出所有人的名字
		}
		printf("\n");
	} 
	return 0;
} 

第二题是图像处理的题,直接放弃

第三题:
瓜农王大爷去年种西瓜赚了不少钱。看到收入不错,今年他又重新开辟了n个西瓜地。
为了能给他的n个西瓜地顺利的浇上水,对于每个西瓜地他可以选择在本地打井,也可以修管道从另一个瓜地(这个瓜地可能打了井;也可能没打井,他的水也是从其他瓜地引来的)将水引过来。
当然打井和修管道的费用有差别。已知在第i个西瓜地打井需要耗费wi元,在第i、j个西瓜地之间修管道需要耗费pi,j元。
现在的问题是:王大爷要想使所有瓜地都被浇上水,至少需要花费多少钱(打井与修管道的费用和)?
由于瓜地较多,王大爷无法选择在哪些(个)瓜地打井,哪些西瓜地之间修管道。
请你编程帮王大爷做出决策,求出最小费用。
输入格式
第1行,一个正整数n,代表西瓜地的数量。
以下n行,依次给出整数w1…wn(每块西瓜地的打井费用)。
紧接着是一个nn的整数矩阵,矩阵的第i行第j列的数代表pi,j(两块西瓜地之间建立管道的费用)。每行的两个数之间有一个空格隔开。
*

//最小生成树是整个图所有路径之和最小,而最短路径是两点之间路径最短,所以此题是最小生成树
//最小生成树有prim和kruskal,prim算法时间复杂度O(v^2),适合顶点数较少,边较多的稠密图,
//kruskal算法O(ElogE),适合稀疏图,边少 一般来说两个算法都可以,此题看这边数很多,我们就选prim算法
//prim算法与Dijkstral类似,Kruskal会用到并查集
/*
思考:本题如果单求出最小生成树,即边的权值之和最小,不一定符合题意,因为题目中还的用上点权,看解答图也知道左小生成树要求
这个图连通,可解答中明明是两个连通分量。
这里想的方法是:依然求出最小生成树,这是所有边权最小的边,然后将其中的一条边与两个顶点的最小点权
比较,如果这条边小于等于它,那么这条边保留,否则取两个顶点的最小点权代替这条边得权值,但是这样不一定能保证图联通
再次审视一下这道题,有一个不同的点是点权值也需要考虑在内,但是prim和Kruskal都是只对边权进行处理,
百度之后看到可以在加一个点,然后将其各个点的点权变为各个点到新点的边,相当于又加了6条边,这样
包括这个点算在内,如果收录的边是一个点与这个点相连的边,就相当于是在这个点上挖了一口井。
/
/

6
5
4
4
3
1
20
0 2 2 2 9 9
2 0 3 3 9 9
2 3 0 4 9 9
2 3 4 0 9 9
9 9 9 9 0 9
9 9 9 9 9 0
/
/

//因为给出的是矩阵存储边的信息,所以直接用prim算法输入会稍微简单些
//prim算法

#include<cstdio>
#include<algorithm>
using namespace std;
const int INF=100000000;
const int maxv=1000;
int n,G[maxv][maxv];//顶点数,存储边信息的二维矩阵 
bool vis[maxv]={false};
int d[maxv];

int prim(){
	fill(d,d+maxv,INF);
	d[0]=0;
	int ans=0;
	for(int i=0;i<=n;i++){
		int u=-1,MIN=INF;
		for(int j=0;j<=n;j++){
			if(vis[j]==false && d[j]<MIN){
				u=j;
				MIN=d[j];
			} 
		}
		if(u==-1) return -1;//图不连通,返回-1 
		vis[u]=true;
		ans+=d[u];
		for(int v=0;v<=n;v++){
			//如果该顶点未访问过,且u可以到达v,且以u为接口让u的邻接点距离集合更近 
			if(vis[v]==false && G[u][v]!=0 && G[u][v]<d[v]){
				d[v]=G[u][v]; 
			}
		}
	}
	return ans; 
}

int main(){
	int w[maxv];
//	fill(G[0],G[0]+maxv*maxv,INF);//将图初始化为全部最大,不初始化也可以,未赋值默认为0 
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		scanf("%d",&w[i]);
	}
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			scanf("%d",&G[i][j]);
	//		if(G[i][j]==0){//因为每次要挑选 
	//			G[i][j]=INF;
	//		}
		}
	}
	//增加了一个新的顶点 ,将每个顶点的权值变成边 
	for(int i=0;i<n;i++){
		G[n][i]=G[i][n]=w[i];
	}
//	G[n][n]=0;
	int ans=prim();
	printf("%d\n",ans); 
	return 0;
}  

//Kruskal算法:

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxv=1000;
const int maxe=300;
int father[maxv];
int n,G[maxv][maxv];//n个顶点
int total_edge=0;//边的总数 
struct edge{
	int u,v;
	int cost;
}E[maxe];

bool cmp(edge a,edge b){
	return a.cost<b.cost;
} 

int findFather(int x){
	int a=x;
	while(x!=father[x]){
		x=father[x];
	}
	//路径压缩
	while(a!=father[a]){
		int z=a;
		father[a]=x;
		a=father[a];
	} 
} 

int Kruskal(){
	int ans=0,num_edge=0; 
	for(int i=0;i<=n;i++){//n+1个顶点 
		father[i]=i;
	}
	sort(E,E+total_edge,cmp);//所有边按权从小到大排序
	for(int i=0;i<total_edge;i++){
		int fau=findFather(E[i].u);
		int fav=findFather(E[i].v);
		if(fau!=fav){//不在同一个连通块中 
			father[fau]=fav;//将其放入同一个连通块中 
			ans+=E[i].cost;//将该边加入最小生成树 
			num_edge++; 
			if(num_edge==n) break;//又加了一个顶点,所以n+1个顶点最小生成树应该有n条边 
		}
	} 
	if(num_edge!=n) return -1;//图不连通返回-1 
	else return ans; 
}

int main(){
	scanf("%d",&n);
	int temp;//暂时存储点权 
	//多了6条边,是从i到n 
	for(int i=0;i<n;i++){
		scanf("%d",&temp);
		E[total_edge].u=i;
		E[total_edge].v=n;
		E[total_edge].cost=temp;
		total_edge++; 
	}
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			scanf("%d",&G[i][j]);
			if(i<j){//只需要上三角 
				E[total_edge].u=i;
				E[total_edge].v=j;
				E[total_edge].cost=G[i][j];
				total_edge++;
			}
		}
	} 
	int ans=Kruskal();
	printf("%d\n",ans); 
	return 0;
}
发布了23 篇原创文章 · 获赞 62 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_42264284/article/details/105644538
今日推荐