天梯选拔赛1

 

一共有15道题,三小时做了三道签到题,excuse me ?补题的时候发现题目思维并不很难,都是经典题,可是我因为练的太少还是需要模板辅助做题,多捞啊。

7-1 两个链表的第一个公共结点 (20 分)

输入两个链表L1、L2,找出它们的第一个公共结点。

输入格式:

输入在第一行分别给出 L1 的头结点地址、L2的头结点地址和结点总个数 N(<=105)。
一个结点的地址是非负的 5 位整数,空地址 NULL 用 −1 来表示。
随后 N 行,每行按以下格式描述一个结点: Address Data Next
其中Address是该结点的地址,Data是整数(不超过int),Next是下个结点的地址。

输出格式:

输出第一个公共结点的值,并给出下一个结点的地址,两个值之间用空格隔开。若两个结点没有公共结点,输出-1。

 

输入样例:

00100 55555 8
00000 4 99999
00100 1 12309
68237 6 -1
75637 8 99999
33218 3 00000
55555 7 75637
99999 5 68237
12309 2 33218

输出样例:

5 68237

 

题解:此题和考试前一天做的天梯赛练习赛的题很像,不是用数据结构的链表指针来做,而是用结构体来存储数据值和下一个节点的地址。根据此法建立第一条链,最初卡了很长时间是因为我以为比较是否有公共节点是比较data,真傻啊,WA数次才想起来看地址。还有一个需要注意的点是前导0.

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int maxn=1e9+10;
bool vis[maxn]={false};
struct node
{
	int adress;
	int key;
	int next;
}nodes[100000];
int start1,start2,n;
int main()
{
//	memset(nodes,0,sizeof(nodes));
	bool flag=true;
	cin>>start1>>start2>>n;
	for(int i=0;i<n;i++)
	{
		int a,b,c;
		cin>>a>>b>>c;
		nodes[a].adress=a;
		nodes[a].key=b;
		nodes[a].next=c;
	}
	for(int i=start1;i!=-1;i=nodes[i].next)
	{
		vis[nodes[i].adress]=true;
	}
	for(int i=start2;i!=-1;i=nodes[i].next)
	{
		if(vis[nodes[i].adress])
		{
			flag=false;
			if(nodes[i].next!=-1)
			{
			printf("%d %05d",nodes[i].key,nodes[i].next);
			}
			else
			{
				printf("%d -1",nodes[i].key);
			}
			
			break;
		}
	}
	if(flag)
	{
		cout<<"-1";
	}
	//system("pause");
	return 0;
}
	
	
	

 

7-2 洗牌 (15 分)

小弱T在闲暇的时候会和室友打扑克,输的人就要负责洗牌。虽然小弱T不怎么会洗牌,但是他却总是输。
渐渐地小弱T发现了一个规律:只要自己洗牌,自己就一定会输。所以小弱T认为自己洗牌不够均匀,就独创了一种小弱洗牌法。
小弱洗牌法是这样做的:
先用传统洗牌法将52张扑克牌(1到K各四张,除去大小王)打乱,放成一堆,然后每次从牌堆顶层拿一张牌。
如果这张牌的大小是 P(1到K的大小分别为1到13),那么就把这张牌插入到当前手中第P张牌的后面。
如果当前手中不足P张牌,那么就把这张牌放在最后。
现在给你一对已经被打乱的牌,请你用小弱洗牌法进行洗牌,然后输出最后生成的序列。

注意:小弱可能在第一次洗牌时弄丢了某些牌,这时请你输出一个-1来提醒他牌的数目不够。

输入格式:

测试数据的输入含N个用空格隔开的字符串表示牌堆从顶至底的每张扑克(1到K中的某个)。可能有多行。

输出格式:

如果N为52,输出用小弱洗牌法洗牌后的序列,每个字符串用空格隔开。 否则请输出一个-1.

输入样例:

4 6 K Q 5 1 Q 9 7 9 K 3 J 1 2 3 5
2
3 5 7 Q 7 10 8 4 9 7 8 9 4
10 6 2 8 2 10 10 Q 5 K J 1
J 8 3 K 4 1 6 J 6

输出样例:

4 1 1 1 3 4 6 6 2 2 2 5 J 3 8 4 4 6 K J 8 J 10 10 K Q 2 5 7 8 10 9 3 7 9 8 7 1 10 5 6 3 Q K Q 5 Q 7 9 9 J K

题解:此题为模拟题,关键是要会运用List,而我根本不知道这东西??双向链表,STL中还可以直接根据位置来进行插入,之前都没自学过。输入问题:此题是运用输入流输入,比赛的时候根本没想到这一点直接放弃思考了。自己做题过程遇到一个误区就是我以为10是一个字符,其实是两个。代码是标程。

#include<iostream>
#include<list>
#include<stdio.h>
#include<algorithm>
using namespace std;
char arr[600];
int cnt=0;
int main()
{
	list<char> l;
	list <char>:: iterator it = l.begin();
	while(!cin.eof())
	{
		cin>>arr[cnt];
		/*
		if(arr[cnt]=='0')
		{
			arr[cnt-1]=10;
			continue;
		}*/
		if (arr[cnt] == '0') {
			arr[cnt - 1] = 'A';
			continue;
		}
		cnt++;
	}
	if(cnt!=53)
	{
		cout<<"-1"<<endl;
		return 0;
	}
	for(int i=0;i<52;i++)
	{
		int num=arr[i]-'0';
		if (arr[i] == 'A')
			num = 10;
		if(arr[i]=='J')
		{
			num=11;
		}
		if(arr[i]=='Q')
		{
			num=12;
		}
		if(arr[i]=='K')
		{
			num=13;
		}
		if(l.size()<=num)
		{
			l.push_back(arr[i]);
		}
		else 
		{
			it = l.begin();
			for (int j = 0; j < num; j++)
				it++;
			l.insert(it,arr[i]);
		}
	}
		int j = 0;
	for(it = l.begin(); it!= l.end(); it++) {
	//	printf("%c", *it);
			if (*it != 'A')
			printf("%c", *it);
		else
			printf("10");
		if (j != 51)
		{
			printf("%c",  ' ');
		}
		else
			printf("\n");
		j++;
	}
	//system("pause");
	return 0;
	
}

7-3 最长单词 (5 分)

编写一个函数,输入一行字符,将此字符串中最长的单词输出。

输入格式:

输入仅一行,多个单词,每个单词间用一个空格隔开。单词仅由小写字母组成。所有单词的长度和不超过100000。

输出格式:

如有多个最长单词,输出最先出现的。

输入样例:

I am a student

输出样例:

student

题解:此题是本场签到题,然而我做了得有40分钟。我以为通过空格来区分各个单词,但是忘记了最后一个单词后面没有空格。

#include<iostream>
#include<string>
#include<cstring>
using namespace std;
//char ss[100001];
int main()
{
	string aa;
	getline(cin,aa);
	string s;
	string t;
	int ans=0;
	for(int i=0;i<aa.length();i++)
	{
		if(aa[i]!=' '&&(i!=aa.length()-1))
		{
			s+=aa[i];
		}
		else 
		{
			if(i==aa.length()-1)
			{
				s+=aa[i];
			}
			if(s.length()>ans)
			{
				t=s;
				ans=s.length();
			
			}
				s="";
		}
		
	}
	cout<<t;



	//system("pause");
	return 0;
}

7-5 跨海大桥 (30 分)

Kyrie居住在以小岛组成的国家中,可以将这个国家看成一个n x n的矩阵。近日,该国养老院科学家李院士掌握了一门尖端科技,可以在两个岛屿之间修建海上列车(起点和终点必须在陆地上),假设从(x​1​​,y​1​​)修建到(x​2​​,y​2​​)那么其费用为(x​1​​−x​2​​)​2​​+(y​1​​−y​2​​)​2​​,现在在这个岛屿上有两座城镇a和城镇b,请设计一个算法,帮忙算出城镇a到城镇b需要修建的桥的最小费用。( 只能修一条)

输入格式:

第一行五个整数分别是n(1≤n≤50),x​1​​,y​1​​,x​2​​,y​2​​,分别代表国家大小n,城镇a和城镇b的位置,接下来n行每行n个数代表城镇的地图,其中0代表是陆地,1代表是海。数据保证城镇一定是在陆地上,且不在同一点。

输出格式:

输出一个整数

输入样例:

5
1 1
5 5
00001
11111
00111
00110
00110

输出样例:

10

7-5 跨海大桥 (30 分)

Kyrie居住在以小岛组成的国家中,可以将这个国家看成一个n x n的矩阵。近日,该国养老院科学家李院士掌握了一门尖端科技,可以在两个岛屿之间修建海上列车(起点和终点必须在陆地上),假设从(x​1​​,y​1​​)修建到(x​2​​,y​2​​)那么其费用为(x​1​​−x​2​​)​2​​+(y​1​​−y​2​​)​2​​,现在在这个岛屿上有两座城镇a和城镇b,请设计一个算法,帮忙算出城镇a到城镇b需要修建的桥的最小费用。( 只能修一条)

输入格式:

第一行五个整数分别是n(1≤n≤50),x​1​​,y​1​​,x​2​​,y​2​​,分别代表国家大小n,城镇a和城镇b的位置,接下来n行每行n个数代表城镇的地图,其中0代表是陆地,1代表是海。数据保证城镇一定是在陆地上,且不在同一点。

输出格式:

输出一个整数

输入样例:

5
1 1
5 5
00001
11111
00111
00110
00110

输出样例:

10

题解:这题目卡了我一天了。运用搜索求出起点和终点的联通块,从这些点之中找出最近的两个点。最终看别人代码才知道我输入错了,数组类型应该开char。然后就是卡在第二个样例上,应该先把递归到的点加入vector。此题dfs简单一些。我却一直杠bfs

//bfs
#include<iostream>
#include<queue>
#include<vector>
#include<cstring>
#include<string>
using namespace std;
struct node
{
	int x;
	int y;
}s,t,nodes;
int n;
char maze[60][60];
bool inq[60][60]={false};
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
bool flag=false;
vector<node>qi;
vector<node>zhong;
int num=1000000000;
int dist(node a,node b)
{
	int ans=(b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y);
	return ans;
}
bool test(int x,int y)
{
	if(x>n||x<=0||y>n||y<=0)
	{
		return false;
	}
	if(maze[x][y]=='1')
	{
		return false;
	}
	if(inq[x][y]==true)
	{
		return false;
	}
	return true;
}
void bfs(node start,int num)
{
		if(num==1)
				{
				
				qi.push_back(start);
			}
			else if(num==2)
			{
				zhong.push_back(start);
			}
	queue<node> q;
	q.push(start);
	inq[start.x][start.y]=true;
	while(!q.empty())
	{
		node top=q.front();
		q.pop();

		for(int i=0;i<4;i++)
		{
			int newx=top.x+dx[i];
			int newy=top.y+dy[i];
			if(test(newx,newy))
			{
				nodes.x=newx;
				nodes.y=newy;
				q.push(nodes);
				if(num==1)
				{
				
				qi.push_back(nodes);
			}
			else if(num==2)
			{
				zhong.push_back(nodes);
			}
			
				inq[newx][newy]=true;
				
			}
		}
	}
}

int main()
{
	cin>>n;
	cin>>s.x>>s.y>>t.x>>t.y;
    getchar();
	for(int i = 1; i <= n; i++){

		for(int j = 1 ; j <= n; j++){

			cin>> maze[i][j];

		}

	}
	
	bfs(s,1);
	bfs(t,2);

	
	
	for(int i=0;i<qi.size();i++)
		{
			for(int j=0;j<zhong.size();j++)
			{
				num=min(num,dist(qi[i],zhong[j]));
			}
		}
		cout<<num<<endl;

	return 0;
}

 

//dfs

#include <iostream>

#include <vector>

#include <cmath>

#include <time.h>

#include <stdlib.h>

#include <algorithm>

#include <cstring>

#include <queue>

#include <string>

#include <stdio.h>

#include <iomanip>

#include <stack>





#define pi 3.1415926

#define fre freopen("C:\\Users\\75812\\Desktop\\cin.txt", "r", stdin)



using namespace std;



int  vis[55][55];

char map[55][55];

int b[4][2] = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}};

int n;



struct node{

	int x, y;

};



vector<node> v1, v2; // v1存放与a联通的点,v2存放与b联通的点 



void dfs(int x, int y, int num){

	node node_1;

	node_1.x = x, node_1.y = y;

	if(num == 1)

		v1.push_back(node_1);

	else

		v2.push_back(node_1);

	vis[x][y] = num;

	for(int i = 0; i < 4; i++){

		int fx = x + b[i][0], fy = y + b[i][1];

		if(vis[fx][fy] == 0 && fx > 0 && fx <= n && fy > 0 && fy <= n && map[fx][fy] == '0')

			dfs(fx, fy, num);

	}

}





int main(){

//	fre;

	cin>> n;

	int x1, x2, y1, y2;

	cin>> x1>> y1>> x2>> y2;

	getchar();

	for(int i = 1; i <= n; i++){

		for(int j = 1 ; j <= n; j++){

			cin>> map[i][j];

		}

	}

	memset(vis, 0, sizeof(vis));

	dfs(x1, y1, 1); // 标记与a联通的点为1并放入v1 

	dfs(x2, y2, 2); // 。。。。。。 

	long long min = 1000000;

	for(int i = 0; i < v1.size(); i++){ // 二重循环遍历所有点找出两个联通块之间距离的最小值 

		for(int j = 0; j < v2.size(); j++){

			long long d = (v1[i].x - v2[j].x) * (v1[i].x - v2[j].x) + (v1[i].y- v2[j].y) * (v1[i].y- v2[j].y);

			if(d < min)

				min = d;

		}

	}

	cout<< min<< endl;

	return 0;

}

 

 

7-7 过河 (20 分)

有N个人想要过一条河,但是他们只有一条最多载两人的船。因此必须想出一个调度船来回的方法让每个人都能过河。每个人都有自己的划船速度,且同一条船上的两个人取决于慢者的速度。你的任务就是想出一个每人都能过河的最快策略。

输入格式:

输入的第一行是一个正整数T(1 <= T <= 20),表示测试用例的组数。下面是T组用例。每个用例的第一行是正整数N,第二行是N个正整数表示每个人的划船速度。每组用例不会超出1000个人,每个人的划船时间不会超过100秒。

输出格式:

对于每个用例,输出所有N个人都能过河的最短时间(秒)。

输入样例:

2
3
1 3 7
4
1 2 5 10

输出样例:

11
17


题解:此题没有思路,思路是百度的。先将各个人的过河时间从小到大排序,a0,a1,a2,a3......an-2,an-1.为了保证最优最后一步一定要是a0和a1一起回来。先将sum加上a1。

过河两种方法,每次过两个人(所以需要N-=2):
1.a0和a1过去,a0回来,aN-2和aN-1过去,a1回来,所花时间sum+=a[1]+a[0]+a[N-1]+a[1]
2.a0和aN-1过去,a0回来和aN-2过去,a0再回来,所花时间sum+=a[N-1]+a[0]+a[N-2]+a[0]
 

最后一步的时候如果剩下三个人,需要a0把他送过去再回来,否则就a0,a1一起过去。

#include<iostream>
#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<math.h>
using namespace std;
int main(){
int i,j,k;
int n;
scanf("%d",&n);
while(n--){
int m;
scanf("%d",&m);
int a[1005];
for(i=0;i<m;i++)
scanf("%d",&a[i]);
sort(a,a+m);
if(m<=2){
printf("%d\n",a[m-1]);
continue;
}
int sum=a[1];
while(m>3){
  if(a[0]+a[m-1]+a[0]+a[m-2]>a[0]+a[m-1]+a[1]+a[1])
          sum+=a[0]+a[m-1]+a[1]+a[1];
  else
      sum+=a[0]+a[m-1]+a[0]+a[m-2];
  m-=2;
}
if(m==3)
sum+=a[m-1]+a[0];
printf("%d\n",sum);
}
return 0;
}

 

7-8 过年了,回家吧 (30 分)

小CC的家离学校有1000多公里,坐火车要数十个小时。每年春运之时,小CC总要绞尽脑汁寻找最合适的换乘路线。

小CC的换乘问题抽象如下:

地图上有N个城市,M条交通路线将城市两两相连。小CC需要经过若干条交通路线,从城市S回到城市T。途径每条交通路线都会消耗一定时间,在中转城市换乘也需要消耗一定时间,起点和终点的换乘时间不计算在内。现在请你编写程序,帮小CC规划回家的路,这条路必须是耗时最短的路径,如果有耗时相同的多条路径,选择其中换乘最少的一条。

例如,小CC要从厦门回到大同,下图是小CC回家时的线路图,图中的方块表示换乘时间,线路上的数字表示线路的旅途时间。

(本图仅作示意,不代表真实旅行时间和实际地理位置)

火车线路图.png

从图中可以看出,有多条路径可以选择,但是耗时最短的路径(耗时375)只有两条:

  1. 厦门->北京->大同,路途时间227+70,换乘时间78;
  2. 厦门->南昌->郑州->大同,旅途时间65+64+125,换乘时间40+81。

此时,小CC会选择换乘次数较少的第1条线路。

输入格式:

第一行为1个正整数N≤500,表示城市个数。

然后是N行,每行依次是城市名称和换乘时间。城市名称不超过40个字符,由大小写字母组成,换乘时间是不大于10​4​​的正整数。

接下来是一个正整数M≤3N,表示交通路线条数。

接着M行,每行三个元素,依次是两个城市名称和路线耗时(不大于10​4​​的正整数)。

最后一行依次给出起点S和目的地T的城市名称。

输出格式:

如果存在一条从起点到终点耗时最短的路线

第一行输出耗时(起点终点的中转时间不计算在内)。

第二行按照从起点到终点的顺序输出沿途城市(包括起点终点),用->链接。

如果有多条耗时最短的路线,输出中转次数最少的一条。

如果不存在一条从起点到终点的路线

输出一行:“Why not go home by plane?"

输入样例1:

示意图中的例子。

10
Datong 52
Xuzhou 87
Hefei 71
Nanchang 40
Zhengzhou 81
Shijiazhuang 56
Taiyuan 45
Nanjing 43
Beijing 78
Xiamen 55
22
Xiamen Beijing 227
Beijing Nanjing 170
Xiamen Hefei 95
Zhengzhou Shijiazhuang 41
Beijing Datong 70
Beijing Taiyuan 34
Taiyuan Datong 65
Taiyuan Shijiazhuang 19
Nanjing Hefei 10
Xuzhou Nanchang 102
Beijing Shijiazhuang 25
Xuzhou Beijing 157
Zhengzhou Xuzhou 37
Xiamen Xuzhou 139
Beijing Nanchang 201
Nanchang Xiamen 65
Zhengzhou Nanchang 64
Datong Zhengzhou 125
Hefei Xuzhou 46
Shijiazhuang Nanjing 198
Taiyuan Zhengzhou 43
Hefei Beijing 165
Xiamen Datong

输出样例1:

375
Xiamen->Beijing->Datong

输入样例2:

非连通图。仅有两条边,乌鲁木齐(新疆)-阿拉山口(新疆),乌兰察布(原称集宁,内蒙古)-呼和浩特(内蒙古)。不存在阿拉山口-乌兰察布路径。

4
Alashankou 50
Urumchi 65
Hohhot 69
Ulanqab 75
2
Urumchi Alashankou 96
Hohhot Ulanqab 125
Alashankou Ulanqab

输出样例2:

Why not go home by plane?

题解:此题是最短路的经典题。需要注意的是要输出中转次数最少的路径。这时候需要diskjia+dfs。用vector存储路径。考试之前还看了,看了又有什么用呢,自己还是写不出来。还有一点就是把地名转换成编号需要map

#include<iostream>
#include<string>
#include<map>
#include<vector>
using namespace std;
const int maxn=1000;
const int INF=1000000000;
int g[maxn][maxn];
int d[maxn];
int w[maxn];
bool vis[maxn]={false};
vector<int> pre[maxn];
map<string,int> STI;
map<int,string>ITS;
int n;
vector<int> temppath,path;
int start;
int eend;
int mintime=INF;
void disk(int s)
{
	fill(d,d+maxn,INF);
	d[s]=0;
	w[s]=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 ;
		vis[u]=true;
		for(int v=0;v<n;v++)
		{
			if(vis[v]==false&&g[u][v]!=INF)
			{
				if(d[u]+g[u][v]+w[v]<d[v])
				{
					d[v]=d[u]+g[u][v]+w[v];
					//w[v]=w[u]+w[v];
					pre[v].clear();
					pre[v].push_back(u);
				}
				else if(d[u]+g[u][v]+w[v]==d[v])
				{
					pre[v].push_back(u);
				}
			}
		}
	}
}
			
void dfs(int v)
{
	if(v==start){
		temppath.push_back(v);
		int temptime=0;
		for(int i=temppath.size()-1;i>=0;i--)
		{
			temptime++;
		}
		if(temptime<mintime)
		{
			mintime=temptime;
			path=temppath;
		}
		temppath.pop_back();
		return ;
	}
	temppath.push_back(v);
	for(int i=0;i<pre[v].size();i++)
	{
		dfs(pre[v][i]);
	}
	temppath.pop_back();
}
int main()
{
	fill(g[0],g[0]+maxn*maxn,INF);
	
	cin>>n;
	for(int i=0;i<n;i++)
	{
		string s;
		cin>>s;
		int weight;
		cin>>weight;
		STI[s]=i;
		ITS[i]=s;
		w[i]=weight;
	}
	int m;
	cin>>m;
	for(int i=0;i<m;i++)
	{
		string a,b;
		int time;
		cin>>a>>b>>time;
		int id1=STI[a];
		int id2=STI[b];
		g[id1][id2]=g[id2][id1]=time;
	}
	string s,t;
	cin>>s>>t;
	 start=STI[s];
	 eend=STI[t];
	disk(start);
	if(d[eend]==INF)
	{
		cout<<"Why not go home by plane?";
	}else
	{
	dfs(eend);
	cout<<d[eend]-w[eend]<<endl;
	for(int i=path.size()-1;i>0;i--)
	{
		cout<<ITS[path[i]]<<"->";
	}
	cout<<ITS[path[0]];
	}
	//system("pause");
	return 0;
	
}

7-9 寻宝路线 (30 分)

在一个m行n列方格矩阵中,每一个方格内摆放着价值不等的宝贝(价值可正可负),让小明感到好奇的是,从左上角到达右下角的所有可能路线中,能捡到宝贝的价值总和最大是多少?而且这种达到最大值的路线 又有多少条?【注意:只能从一个格子向下或向右走到相邻格子,并且走到的格子宝贝一定会被捡起。】

输入格式:

第一行为整数m,n(均不大于100),下一行开始会有一个m行n列的整数方阵,对应方格矩阵中的宝贝价值(这些值的绝对值都不超过500)。

输出格式:

单独一行输出2个整数,分别为能捡到宝贝价值总和的最大值和达到最大值的路线数量,2个整数间隔一个空格。

输入样例:

在这里给出一组输入。例如:

4  5
2  -1  6  -2  9
-3  2  5  -5  1
5   8  3  -2  4
5   2  8  -4  7

输出样例:

对应的输出为:

26 3

题解:此题利用动态规划,比赛的时候还一直在刚bfs。其实和数塔很像,但是此题是知道最初的状态,然后反向dp。

#include<iostream>
using namespace std;
const int maxn=110;
int n,m;
int g[maxn][maxn];
int dp[maxn][maxn];
int maxvalue=0;
int num[maxn][maxn];
int main()
{
	cin>>m>>n;
	num[0][0]=1;
	for(int i=0;i<m;i++)
	{
		for(int j=0;j<n;j++)
		{
			cin>>g[i][j];
		}
	}
	dp[0][0]=g[0][0];
	for(int i=0;i<m;i++)
	{
		for(int j=0;j<n;j++)
		{
			if(i==0)
			{
				if(j-1>=0)
				{
					dp[i][j]=dp[i][j-1]+g[i][j];
					num[i][j]=num[i][j-1];
				}
			}
			else if(j==0)
			{
				if(i-1>=0)
				{
					dp[i][j]=dp[i-1][j]+g[i][j];
					num[i][j]=num[i-1][j];
				}
			}
			else if(dp[i-1][j]!=dp[i][j-1])
			{
				
				dp[i][j]=max(dp[i-1][j],dp[i][j-1])+g[i][j];
				if(dp[i-1][j]>dp[i][j-1])
				{
					num[i][j]=num[i-1][j];
				}
				else num[i][j]=num[i][j-1];
			}
			
			else if(dp[i-1][j]==dp[i][j-1])
			{
			
				dp[i][j]=dp[i-1][j]+g[i][j];
				num[i][j]=num[i][j-1]+num[i-1][j];
				
			}
		}
	}
	cout<<dp[m-1][n-1]<<" "<<num[m-1][n-1];
	//system("pause");
	return 0;
}

7-10 参与者人数 (20 分)

临沂大学有很多社团,一个学生可能会加入多个社团。为了活跃大学生业余生活,增强体育运动积极性,临沂大学读书社团决定举行大学生跳绳比赛,要求该社团成员必须参加。为了扩大影响,要求只要其他社团有一个人参加,那么该社团中的每一个人都必须参加。求参加比赛至少多少人?

输入格式:

输入第一行包含两个整数n和m,n(0 < n <= 30000)表示学生的数目,m(0 <= m <= 500)表示社团的数目。每个学生都有一个唯一的编号,编号取值为0到n-1,编号为0的社团是读书社团。 接下来有m个社团的名单,每个社团的名单在输入中为一行。每一行先输入一个数k表示社团总人数。接着是社团中k个成员的编号。

输出格式:

输出一个数占一行,表示参加比赛的总人数。

输入样例:

100 4 
2 1 2 
5 10 13 11 12 14 
2 0 1 
2 99 2 

输出样例:

4
题解:此题是并查集经典题,比赛的时候一直在打标记模拟。题目中的编号为0的社团是读书社团,理解错了意思,看了题解才知道他是把第一行输入的社团当作读书社团。标记读书社团的第一个人。然后将每个社团中的人都连起来。最后根据标记的人来寻找堆。

#include<iostream>
using namespace std;
int father[30005];
int get(int a)
{
	while(father[a]!=a)
	{
		a=father[a];
	}
	return a;
}
void merge(int a,int b)
{
	int gx=get(a);
	int gy=get(b);
	if(gx!=gy)
	{
		father[gx]=gy;
	}
}


int main()
{
	int n,m;
	int t;
	cin>>n>>m;
	for(int i=0;i<n;i++)
	{
		father[i]=i;
	}
	for(int i=0;i<m;i++)
	{
		int k,x;
		cin>>k>>x;
		if(i==0)
		{
			t=x;
		}
		for(int j=1;j<k;j++)
		{
			int a;
			cin>>a;
			merge(a,x);
		}
	}
	int num=0;
	for(int i=0;i<n;i++)
	{
		if(get(t)==get(i))
		{
			num++;
		}
	}
	cout<<num;
//	system("pause");
	return 0;
}

7-11 工作分配问题 (20 分)

设有n件工作分配给n个人。将工作i分配给第j个人所需的费用为cij 。 设计一个算法,对于给定的工作费用,为每一个人都分配1 件不同的工作,并使总费用达到最小。

输入格式:

输入数据的第一行有1 个正整数n (1≤n≤20)。接下来的n行,每行n个数,表示工作费用。

输出格式:

将计算出的最小总费用输出到屏幕。

输入样例:

在这里给出一组输入。例如:

3
10 2 3
2 3 4
3 4 5

输出样例:

在这里给出相应的输出。例如:

9

题解:此题是搜索+剪枝。自己做的超时了百度一下才知道少剪了条件

if(value>minvalue)
    {
        return;
    }

运用dfs

#include<iostream>
#include<cstring>
using namespace std;
int a[25][25];
bool vis[25];
int minvalue=1000000000;
int n;
int value;
void dfs(int work,int people)
{
	
	if(work==n-1)
	{
		value+=a[work][people];
		minvalue=min(value,minvalue);
		
	
		return ;
	}
	value+=a[work][people];
	if(value>minvalue)
	{
		return;
	}

	for(int i=0;i<n;i++)
	{
		if(vis[i]==false)
		{
			vis[i]=true;
			
			dfs(work+1,i);
			value-=a[work+1][i];
		
			vis[i]=false;
		}
	}
}

int main()
{
	cin>>n;
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<n;j++)
		{
			cin>>a[i][j];
		}
	}
	for(int i=0;i<n;i++)
	{
		
		
		for(int j=0;j<n;j++)
		{
			vis[j]=false;
		}
		vis[i]=true;
		value=0;
		dfs(0,i);
		
	}
	cout<<minvalue;
//	system("pause");
	return 0;
}

7-12 基于DFA的字符串识别 (15 分)

确定性有穷状态自动机(DFA)可以理解为由若干个状态构成的,且能够通过一定的规则自动在状态间转换的结构。其中一种状态遇到某一种标志只可能转换为一种状态,即确定性。

下图是一个DFA的示意图。

29.png

初始状态为d​0​​,此后逐个读入字符。d​0​​遇到a后转换为d​1​​;d​1​​遇到b后转换为状态d​2​​;d​2​​遇到b后仍为d​2​​;d​2​​遇到c后转化为d​3​​。d​1​​遇到c后转换为状态d​3​​;等等 ,依此类推。

其中带双圆圈的代表终态。当一个字符串读取结束时刚好到达终态,则其能通过这个DFA的检测。

上图的自动机可以用正则表达式a(b|c)*表示。

现在给出一个DFA,和一个字符串,判断其能否通过该DFA的检测。

输入格式:

第一行给出整数 M 和 N (0<M<=200,0<N<=3000),分别代表DFA的状态数和转换规则数。其中状态用数字 0, 1, 2, … ,M−1 表示,初始状态为0

第二行给出整数 K 和 K 个整数t​1​​, t​2​​, …, t​K​​。表示有 K 个终态,t​1​​ - t​K​​表示终态的编号。

之后 N行,每行给出一个规则,其格式为:

状态1 状态2 字符

其中字符为大小写字母 (a-z和A-Z)。 比如0 1 a,表示状态0遇到字符a转换为状态1。

接下来给出整数 Q,表示有 Q 个字符串待检测。

最后 Q 行每行给出一个字符串。

输出格式:

对于每个待检测字符串,输出 Yes 或 No 表示能否通过检测。

输入样例:

4 7
3 1 2 3
0 1 a
1 2 b
1 3 c
2 3 c
3 2 b
3 3 c
2 2 b
4
abc
abdc
a
aab

输出样例:

Yes
No
Yes
No

题解:此题为普通模拟题,存转换规则可让字母减去‘A’存入二维数组;

#include<iostream>
#include<cstring>
#include<string>
using namespace std;
const int INF=10000;
int g[210][100];
bool gl[210][100]={false};
int m,n;
bool vis[210]={false};
int main()
{

	cin>>m>>n;

	int k;
	cin>>k;
	for(int i=0;i<k;i++)
	{
		int temp;
		cin>>temp;
		vis[temp]=true;
	}
	for(int i=0;i<n;i++)
	{
		int a,b;
		char c;
		cin>>a>>b>>c;
		int d=c-'A';
		//cout<<d<<endl;
		g[a][d]=b;
		gl[a][d]=true;
	}
		int q;
		cin>>q;
		for(int i=0;i<q;i++)
		{
			char hh[100];
			cin>>hh;
			bool flag=0;
			int now=0;
			for(int j=0;j<strlen(hh);j++)
			{
				
				int pp=hh[j]-'A';
		
				if(gl[now][pp]==false)
				{
					flag=true;
					break;
				}
				else
				{
					now=g[now][pp];
						//cout<<now<<endl;
				}
				
			}
			
			 if(flag==false&&vis[now]==true)
			{
				cout<<"Yes"<<endl;
			}
			else
			{
			cout<<"No"<<endl;
			 }
		}
		//system("pause");
		return 0;
}

7-13 求最大、次大和第3大的值 (25 分)

本题目要求读入n个整数,要求用最少的比较次数,输出它们的最大值、第2大的值和第3大的值。例如,对于13 13 1 10 34 10这6个数,最大值为34,第2大的值为13,第3大的值为10。

输入格式:

输入有两行。第一行为整数个数n(≤1 000 000),第二行给出n个以空格分隔的整数。

输出格式:

对每一组输入,在一行中输出最大值、第2大的值和第3大的值值,中间以一个空格分隔,但行尾没有多余空格。 如果输入数据不足三个,则输出“Invalid Input”。 如果没有第3大的值,则输出“There is no third largest element”。 如果没有第2大和第3大的值,则输出“There is no second largest and third largest element”。

输入样例:

6
13 13 1 10 34 10 

输出样例:

34 13 10 

题解:此题补题得时候用了直接插入排序,还是超时。问了同学才知道原来是在输入的时候用三个变量直接记录第一大第二大,第三大

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
int a[1000010];
int main()
{
	int n;
	//cin>>n;
	scanf("%d",&n);
	if(n<3)
	{
		cout<<"Invalid Input";
	}
	else
	{
		bool flag=1;
		int a3=0;
		int a2=0;
		int a1=0;
		int t;
		for(int i=0;i<n;i++)
		{
			//cin>>t;
			scanf("%d",&t);
			if(t>=a3)
			{
				if(t!=a3)
				{
					a1=a2;
					a2=a3;
					a3=t;
					
				}
				continue;
			}else if(t>=a2)
			{
				if(t!=a2)
				{
					a1=a2;
					a2=t;
					
				}
				continue;
			}
			else if(t>a1)
			{
				a1=t;
			}
		}
		/*if(a1!=0&&a2!=0&&a3!=0)
		{
			cout<<a3<<" "<<a2<<" "<<a1;
		}
		else if(a3!=0&&a2!=0&&a1==0)
		{
			cout<<"There is no third largest element";
		}
		else if(a3!=0&&a2==0&&a1==0)
		{
			cout<<"There is no second largest and third largest element";
		}*/
		if(a2==0&&a1==0){
		a1=1;
		flag=0;
		cout<<"There is no second largest and third largest element"<<endl;
	}
	if(a1==0){
		flag=0;
		cout<<"There is no third largest element"<<endl;
	}
	if(flag)
		cout<<a3<<" "<<a2<<" "<<a1;
					
		

	}
	//system("pause");
	return 0;
}

7-14 寻找完美数* (15 分)

所有真因子之和小于其本身的数称为亏数。如:4 的真因子 1、2 之和为 3,小于 4,是亏数。

所有真因子之和大于其本身的数称为盈数。如:12 的真因子 1、2、3、4、6 之和为 16,大于 12,是盈数。

不盈不亏的数,即:所有真因子之和等于其本身的数,称为完美数。如:6 的真因子 1、2、3 之和恰为 6,是完美数。

请编写程序,显示指定范围内的完美数。

输入格式

两个正整数 a 和 b,且 a ≤ b,即区间 [a, b] 的下限和上限。

输出格式

若区间内存在完美数,则在一行内输出全部完美数,以空格间隔。若区间不存在完美数,则输出“None”。

输入样例1

1 30

输出样例1

6 28

输入样例2

100 400

输出样例2

None

题解:普通模拟题,注意输出格式

#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
int main()
{
	bool flag=false;
	int num=0;
	int a,b;
	cin>>a>>b;
	if(a==1)
	{
		a=a+1;
	}
	for(int i=a;i<=b;i++)
	{
		int tmp=sqrt(i);
		int ans=1;
		for(int j=2;j<=tmp;j++)
		{
			if(i%j==0)
			{
				ans+=j+i/j;
			}
		}
		if(ans==i)
		{
			num++;
			if(num==1)
			{
				cout<<i;
			}
			else
			{
			cout<<" "<<i;
			}
			flag=true;
		}
	}
	if(!flag)
	{
		cout<<"None";
	}
	//system("pause");
	return 0;
}

7-15 二叉树上你和我 (20 分)

给出一个n个结点的二叉树的前序和中序遍历,初始权值为0,有如下3个操作分别
1 x val
2 x val
3 x 
分别代表 x的子树的权值都加val包括x。
根到x上的结点都减val包括x。
输出层次x的结点权值总和。不存在该层的话输出-1。

输入格式:

第一行一个n,第二行和第三行分别是前序和中序遍历,接下来为m个操作。1≤n≤30,−100≤val≤100

输出格式:

对于询问3的输出结果。

输入样例:

3
2 1 3
1 2 3
3
1 2 1
2 1 10
3 1

输出样例:

在这里给出相应的输出。例如:

-9

题解:这题真的很麻烦,首先要根据前序序列和中序序列建树,然后采用bfs层次遍历来完善双亲结点和层次的信息。在进行操作的时候,每次都要层次遍历来寻找该节点的位置,再利用队列更新权值。补题时看题马虎忽略了-1的情况WA了很多次

#include<iostream>
#include<queue>
#include<vector>
using namespace std;
struct node
{
	int data;
	node *lchild;
	node* rchild;
	node* parent;
	int quan;
	int num;
};
vector<node*>hh[50];
int pre[50];
int in[50];
int n;
int maxlevel=0;
node*now;
node*creat(int prel,int prer,int inl,int inr)
{
	if(prel>prer)
	{
		return NULL;
	}
	node*root=new node;
	root->quan=0;
	root->data=pre[prel];
	int k;
	for(k=inl;k<=inr;k++)
	{
		if(in[k]==pre[prel])
		{
			break;
		}
	}
	int numleft=k-inl;
	root->lchild=creat(prel+1,prel+numleft,inl,k-1);
	root->rchild=creat(prel+numleft+1,prer,k+1,inr);
	return root;
}
void bfs(node*root)
{
	queue<node*>q;
	q.push(root);
	root->num=1;
	hh[1].push_back(root);
	root->parent=NULL;
	while(!q.empty())
	{
		now=q.front();
		q.pop();
		if(now->lchild!=NULL)
		{
			node *tmp=now->lchild;
			tmp->parent=root;
			tmp->num=now->num+1;
			maxlevel=max(maxlevel,tmp->num);
			hh[tmp->num].push_back(tmp);
			q.push(now->lchild);
		}
		if(now->rchild!=NULL)
		{
			node *tmp=now->rchild;
			tmp->parent=root;
			tmp->num=now->num+1;
			maxlevel=max(maxlevel,tmp->num);
			hh[tmp->num].push_back(tmp);
			q.push(now->rchild);
		}
	}
}
node*ceng(node*root,int b)
{
	queue<node*>q;
	q.push(root);
	while(!q.empty())
	{
		now=q.front();
		q.pop();
		if(now->data==b)
		{
			return now;
		}
		if(now->lchild!=NULL)
		{
			q.push(now->lchild);
		}
		if(now->rchild!=NULL)
		{
			q.push(now->rchild);
		}
	}
}
	
int main()
{
	
	cin>>n;
	for(int i=0;i<n;i++)
	{
		cin>>pre[i];
	}
	for(int j=0;j<n;j++)
	{
		cin>>in[j];
	}
	node*root=creat(0,n-1,0,n-1);
	bfs(root);
	int m;
	cin>>m;
	while(m--)
	{
		int a,b,c;
		cin>>a;
		if(a==1)
		{
			cin>>b>>c;
			now=ceng(root,b);
			queue<node*>qq;
			qq.push(now);
			while(!qq.empty())
			{
				node*ff=qq.front();
				qq.pop();
				ff->quan+=c;
				if(ff->lchild!=NULL)
				{
					qq.push(ff->lchild);
				}
				if(ff->rchild!=NULL)
				{
					qq.push(ff->rchild);
				}
			}
		}
		else if(a==2)
		{
			//cout<<"hh"<<endl;
			cin>>b>>c;
			
			now=ceng(root,b);
			
			queue<node*>qq;
			qq.push(now);
			while(!qq.empty())
			{
				node*ff=qq.front();
		
				qq.pop();
				ff->quan-=c;
			
				if(ff->parent!=NULL)
				{
					qq.push(ff->parent);
				}
				
			}
		}
		else if(a==3)
		{
			cin>>b;
			if(b<=0||b>maxlevel)
			{
				cout<<"-1"<<endl;
			}
			else
			{
			int ans=0;
			for(int i=0;i<hh[b].size();i++)
			{
				ans+=hh[b][i]->quan;
			}
			cout<<ans<<endl;
			}
		}
	}
	//system("pause");
	return 0;
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值