csp201312 题解

本文介绍了五个算法题目,涉及数组、字符串处理、单调栈、动态规划和图论等领域,通过详细的问题分析、解题思路和参考代码,展示了不同类型的算法解决方法。这些题目涵盖了数据结构和算法的基础知识,对于提升编程技能和理解算法应用具有重要意义。
摘要由CSDN通过智能技术生成

出现次数最多的数(csp201312-1) :

问题描述

题目简述

给定n个正整数,找出它们中出现次数最多的数。如果这样的数有多个,请输出其中最小的一个。

输入/输出格式

输入格式:
输入的第一行只有一个正整数n(1 ≤ n ≤ 1000),表示数字的个数。
输入的第二行有n个整数s1, s2, …, sn (1 ≤ si ≤ 10000, 1 ≤ i ≤ n)。相邻的数用空格分隔。
输出格式:
输出这n个次数中出现次数最多的数。如果这样的数有多个,输出其中最小的一个。

样例

输入样例:
6
10 1 10 20 30 20
输出样例:
10

问题分析

解题思路

水题,数据只有1-10000,直接开数组存每个数出现的次数,从前向后遍历一遍就可以了。

参考代码
#include <iostream>
#include <cstring>

using namespace std;

int num[10010];
int n;

int main()
{
	memset(num,0,sizeof(num));
	scanf("%d",&n);
	int a;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a);
		num[a]++;
	}
	int index=0;
	int ans=0;
	for(int i=1;i<10010;i++)
	{
		if(num[i]>ans) 
		{
			index=i;
			ans=num[i];
		}
	}
	printf("%d",index);
	return 0;
}

心得体会

挺简单的题,没啥可说的,写的时候要注意别数组越界就行了。

ISBN号码(csp201312_2):

问题描述

题目简述

每一本正式出版的图书都有一个ISBN号码与之对应,ISBN码包括9位数字、1位识别码和3位分隔符,其规定格式如“x-xxx-xxxxx-x”,其中符号“-”是分隔符(键盘上的减号),最后一位是识别码,例如0-670-82162-4就是一个标准的ISBN码。ISBN码的首位数字表示书籍的出版语言,例如0代表英语;第一个分隔符“-”之后的三位数字代表出版社,例如670代表维京出版社;第二个分隔之后的五位数字代表该书在出版社的编号;最后一位为识别码。
  识别码的计算方法如下:
  首位数字乘以1加上次位数字乘以2……以此类推,用所得的结果mod 11,所得的余数即为识别码,如果余数为10,则识别码为大写字母X。例如ISBN号码0-670-82162-4中的识别码4是这样得到的:对067082162这9个数字,从左至右,分别乘以1,2,…,9,再求和,即0×1+6×2+……+2×9=158,然后取158 mod 11的结果4作为识别码。
  编写程序判断输入的ISBN号码中识别码是否正确,如果正确,则仅输出“Right”;如果错误,则输出是正确的ISBN号码。

输入/输出格式

输入格式:
输入只有一行,是一个字符序列,表示一本书的ISBN号码(保证输入符合ISBN号码的格式要求)。
输出格式:
输出一行,假如输入的ISBN号码的识别码正确,那么输出“Right”,否则,按照规定的格式,输出正确的ISBN号码(包括分隔符“-”)。

样例

输入样例:
0-670-82162-4
0-670-82162-0
输出样例:
Right
0-670-82162-4

问题分析

解题思路

看着有些麻烦的题,其实挺简单的。本质上是一个字符串处理。但是这个字符串的格式太固定了,因此,我这里直接使用一个辅助数组将字符串中有用的数据的位置标出。这样可以省去遍历字符串的时候跳过”-“。其他的都比较简单,对读出的数字先处理到0-9,之后计算与最后一位相比,然后就是mod的结果为10的情况需要特判。

参考代码
#include <iostream>
#include <cstring> 

using namespace std;

char isbn[15];
int pos[11]={-1,1,3,4,5,7,8,9,10,11,13};
char ans;

int main()
{
	memset(isbn,32,sizeof(isbn));
	scanf("%s",&isbn[1]);
	int sum=0;
	for(int i=1;i<=9;i++)
	{
		sum+=i*(isbn[pos[i]]-'0');
	}
	if((isbn[pos[10]]=='X'&&sum%11==10)||(sum%11==isbn[pos[10]]-'0')) printf("Right");
	else
	{
		if(sum%11==10)
		{
			ans='X';
		}
		else 
		{
			ans=sum%11+'0';
		}
		for(int i=1;i<=12;i++)
		{
			printf("%c",isbn[i]);
		}
		printf("%c",ans);
	}
	return 0;
}

心得体会

比较简单的字符串处理的题目,也不是非常的难。总体来说字符串处理还是比较常出的一类题,还得再练一练。

最大的矩形(csp201312-3):

问题描述

题目简述

在横轴上放了n个相邻的矩形,每个矩形的宽度是1,而第i(1 ≤ i ≤ n)个矩形的高度是hi。这n个矩形构成了一个直方图。请找出能放在给定直方图里面积最大的矩形,它的边要与坐标轴平行。

输入/输出格式

输入格式:
第一行包含一个整数n,即矩形的数量(1 ≤ n ≤ 1000)。
第二行包含n 个整数h1, h2, … , hn,相邻的数之间由空格分隔。(1 ≤ hi ≤ 10000)。hi是第i个矩形的高度。
输出格式:
输出一行,包含一个整数,即给定直方图内的最大矩形的面积。

样例

输入样例:
6
3 1 6 5 2 3
输出样例:
10

问题分析

解题思路

比较裸的一道单调栈的题目。思路就是找到第n个高度向左和向右的最大延伸到的位置。然后两个位置相减乘高度即可算出矩形面积。采用单调非递减栈,从前向后遍历,找到每个高度能延伸到的最右边的位置。从后向前遍历,找到每个高度能延伸到的最左边的位置。求出后遍历一遍,找到最大的面积输出即可。

参考代码
#include <iostream>
#include <stack>
#include <cstring>

using namespace std;

int n;
int height[1010];
int left_pos[1010];
int right_pos[1010];
stack<int> s;

void init()
{
	memset(height,0,sizeof(height));
	memset(left_pos,0,sizeof(left_pos));	
	memset(right_pos,0,sizeof(right_pos));
	while(!s.empty()) s.pop();
}

void find_right()
{
	for(int i=1;i<=n;i++)
	{
		if(!s.empty())
		{
			while(!s.empty()&&height[s.top()]>height[i])
			{
				right_pos[s.top()]=i-1;
				s.pop();
			}
		}
		s.push(i);
	}
	while(!s.empty())
	{
		right_pos[s.top()]=n;
		s.pop();
	}
}

void find_left()
{
	for(int i=n;i>=1;i--)
	{
		if(!s.empty())
		{
			while(!s.empty()&&height[s.top()]>height[i])
			{
				left_pos[s.top()]=i+1;
				s.pop();
			}
		}
		s.push(i);
	}
	while(!s.empty())
	{
		left_pos[s.top()]=1;
		s.pop();
	}
}

int find_max()
{
	int ans=-1;
	for(int i=1;i<=n;i++)
	{
		int t=height[i]*(right_pos[i]-left_pos[i]+1);
		ans=max(ans,t);
	}
	return ans;
}

int main()
{
	init();
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&height[i]);
	}
	find_left();
	find_right();
	printf("%d",find_max());
	return 0;
}

心得体会

很久没做单调栈的题目了,感觉用起来有点生疏了。写程序的时候稍微多想了一些时间。但是总体来说还是比较顺利的。

有趣的数(csp201312-4):

问题描述

题目简述

我们把一个数称为有趣的,当且仅当:
  1. 它的数字只包含0, 1, 2, 3,且这四个数字都出现过至少一次。
  2. 所有的0都出现在所有的1之前,而所有的2都出现在所有的3之前。
  3. 最高位数字不为0。
  因此,符合我们定义的最小的有趣的数是2013。除此以外,4位的有趣的数还有两个:2031和2301。
  请计算恰好有n位的有趣的数的个数。由于答案可能非常大,只需要输出答案除以1000000007的余数。

输入/输出格式

输入格式:
输入只有一行,包括恰好一个正整数n (4 ≤ n ≤ 1000)。
输出格式:
输出只有一行,包括恰好n 位的整数中有趣的数的个数除以1000000007的余数。

样例

输入样例:
4
输出样例:
3

问题分析

解题思路

说实话做的时候完全没有思路,然后上网搜了题解,看了之后才知道是dp。。。首先可以发现,每一个有趣的数的最高位一定是2,这样来说,要产生n位有趣的数,可以通过n-1位递推而来。进而可以发现,要满足有趣的数的排列,有六种状态:1.只有2。2.只有2和0。3.只有2和3。4.只含2,0,1。5,只含2,0,3。6.含0,1,2,3。这六种状态在特定的排列下可以生成有趣的数。具体转移方程如下:
f(i,1)=1
f(i,2)=f(i-1,1)+2f(i-1,2)
f(i,3)=f(i-1,1)+f(i-1,3)
f(i,4)=f(i-1,2)+2f(i-1,4)
f(i,5)=f(i-1,3)+2f(i-1,5)
f(i,6)=f(i-1,4)+f(i-1,5)+2f(i-1,6)
初始状态f(1,1)=1,其他均为0,之后状态转移即可,最后结果在f(n,6)中。

参考代码
#include <iostream>
#include <cstring>

using namespace std;

const int mod=1000000007;
long long num[1010][7];
int n;

int main()
{
	scanf("%d",&n);
	memset(num,0,sizeof(num));
	num[1][1]=1;
	for(int i=2;i<=n;i++)
	{
		num[i][1]=1; //只含2的符合排列的i位数 
		num[i][2]=(num[i-1][1]+2*num[i-1][2])%mod; //只含0,2的符合排列的i位数 
		num[i][3]=(num[i-1][1]+num[i-1][3])%mod; //只含2,3的符合排列的i位数 
		num[i][4]=(num[i-1][2]+2*num[i-1][4])%mod; //只含0,1,2的符合排列的i位数 
		num[i][5]=(num[i-1][2]+num[i-1][3]+2*num[i-1][5])%mod; //只含0,2,3的符合排列的i位数 
		num[i][6]=(num[i-1][4]+num[i-1][5]+2*num[i-1][6])%mod; //含0,1,2,3的符合排列的i位数 
	}
	printf("%lld",num[n][6]);
	return 0;
}

心得体会

dp还是不太行,很长时间没做加上学的时候也没学的很透彻。以后这方面的题一定要多练。

I’m stuck!(csp201312-5) :

问题描述

题目简述

给定一个R行C列的地图,地图的每一个方格可能是’#’, ‘+’, ‘-’, ‘|’, ‘.’, ‘S’, ‘T’七个字符中的一个,分别表示如下意思:
  ‘#’: 任何时候玩家都不能移动到此方格;
  ‘+’: 当玩家到达这一方格后,下一步可以向上下左右四个方向相邻的任意一个非’#‘方格移动一格;
  ‘-’: 当玩家到达这一方格后,下一步可以向左右两个方向相邻的一个非’#‘方格移动一格;
  ‘|’: 当玩家到达这一方格后,下一步可以向上下两个方向相邻的一个非’#‘方格移动一格;
  ‘.’: 当玩家到达这一方格后,下一步只能向下移动一格。如果下面相邻的方格为’#’,则玩家不能再移动;
  ‘S’: 玩家的初始位置,地图中只会有一个初始位置。玩家到达这一方格后,下一步可以向上下左右四个方向相邻的任意一个非’#‘方格移动一格;
  ‘T’: 玩家的目标位置,地图中只会有一个目标位置。玩家到达这一方格后,可以选择完成任务,也可以选择不完成任务继续移动。如果继续移动下一步可以向上下左右四个方向相邻的任意一个非’#'方格移动一格。
  此外,玩家不能移动出地图。
  请找出满足下面两个性质的方格个数:
  1. 玩家可以从初始位置移动到此方格;
  2. 玩家不可以从此方格移动到目标位置。

输入/输出格式

输入格式:
输入的第一行包括两个整数R 和C,分别表示地图的行和列数。(1 ≤ R, C ≤ 50)。
接下来的R行每行都包含C个字符。它们表示地图的格子。地图上恰好有一个’S’和一个’T’。
输出格式:
如果玩家在初始位置就已经不能到达终点了,就输出“I’m stuck!”(不含双引号)。否则的话,输出满足性质的方格的个数。

样例

输入样例:
5 5
–±+
…|#.
…|##
S-±T
####.
输出样例:
2

问题分析

解题思路

挺好玩的一道bfs的题目。这个题有两个点:第一个是建图。需要将输入的字符矩阵转换成标准的图模型进行存储。第二点就是dfs的思路:首先还是从S开始bfs,求出S的bfs树。此时如果S的bfs树中没有T,那么就输出不存在。如果可以找到T,那么就对S的bfs树中除了S和T的点都运行一遍bfs,如果发现不能找到T,那么这个点就满足要求。最后输出点的数量即可。

参考代码
#include <iostream>
#include <cstring>
#include <vector>
#include <queue>

using namespace std;

class edge
{
public:
	int from,to;
	edge(){}
	edge(int ff,int tt)
	{
		from=ff;
		to=tt;
	}
};

vector<int> graph[3000];
vector<edge> edges;
queue<int> q;
char g[55][55];
int r,c;
int start,ed;
int dx[4]={0,0,-1,1};
int dy[4]={-1,1,0,0};
int vis[3000];
int vis2[3000];

void init()
{
	for(int i=0;i<3000;i++)
	{
		graph[i].clear();
	}
	edges.clear();
	memset(g,32,sizeof(g));
}

void addedge(int from,int to)
{
	edge e(from,to);
	edges.push_back(e);
	graph[from].push_back(edges.size()-1);
}

void bfs(int s,int mod)
{
	if(mod==0) 
	{
		memset(vis,0,sizeof(vis));
	    while(!q.empty()) q.pop();
        q.push(s);
        vis[s]=1;
        while(!q.empty())
        {
    	    int index=q.front();
    	    q.pop();
    	    for(int i=0;i<graph[index].size();i++)
        	{
    		    edge& e=edges[graph[index][i]];
    		    if(vis[e.to]==0)
    		    {
    		    	vis[e.to]=1;
    			    q.push(e.to);
			    }
	    	}
	    }
	}
	else 
	{
		memset(vis2,0,sizeof(vis2));
	    while(!q.empty()) q.pop();
        q.push(s);
        vis2[s]=1;
        while(!q.empty())
        {
    	    int index=q.front();
    	    q.pop();
    	    for(int i=0;i<graph[index].size();i++)
        	{
    		    edge& e=edges[graph[index][i]];
    		    if(vis2[e.to]==0)
    		    {
    		    	vis2[e.to]=1;
    			    q.push(e.to);
			    }
			    if(vis2[ed]==1) return ;
	    	}
	    }
	}
}

int main()
{
	init();
	scanf("%d %d",&r,&c);
	for(int i=1;i<=r;i++)
	{
		getchar();
		for(int j=1;j<=c;j++)
		{
			scanf("%c",&g[i][j]);
			if(g[i][j]=='S') start=(i-1)*c+j;
			if(g[i][j]=='T') ed=(i-1)*c+j;
		}
	}
	for(int i=1;i<=r;i++)
	{
		for(int j=1;j<=c;j++)
		{
			if(g[i][j]=='+'||g[i][j]=='S'||g[i][j]=='T')
			{
				for(int k=0;k<4;k++)
				{
					int xx=i+dx[k];
					int yy=j+dy[k];
					if(xx>=1&&xx<=r&&yy>=1&&yy<=c&&g[xx][yy]!='#')
					{
						addedge((i-1)*c+j,(xx-1)*c+yy);
					}
				}
			}
			else if(g[i][j]=='-')
			{
				for(int k=0;k<2;k++)
				{
					int xx=i+dx[k];
					int yy=j+dy[k];
					if(xx>=1&&xx<=r&&yy>=1&&yy<=c&&g[xx][yy]!='#')
					{
						addedge((i-1)*c+j,(xx-1)*c+yy);
					}
				}
			}
			else if(g[i][j]=='|')
			{
				for(int k=2;k<4;k++)
				{
					int xx=i+dx[k];
					int yy=j+dy[k];
					if(xx>=1&&xx<=r&&yy>=1&&yy<=c&&g[xx][yy]!='#')
					{
						addedge((i-1)*c+j,(xx-1)*c+yy);
					}
				}
			}
			else if(g[i][j]=='.')
			{
				int xx=i+dx[3];
				int yy=j+dy[3];
				if(xx>=1&&xx<=r&&yy>=1&&yy<=c&&g[xx][yy]!='#')
				{
					addedge((i-1)*c+j,(xx-1)*c+yy);
				}
			}
		}
	}
	bfs(start,0);
	if(vis[ed]==0) printf("I'm stuck!");
	else 
	{
		int cnt=0;
		for(int i=1;i<=r*c;i++)
		{
			if(vis[i]==1&&i!=start&&i!=ed)
			{
				bfs(i,1);
				if(vis2[ed]==0) 
				{
					cnt++;
				}
			}
		}
		printf("%d",cnt);
	}
	return 0;
}

心得体会

这几天练图论的题比较多,所以这个题虽然的第五题,但感觉做的还是很顺利的(也可能题比较简单吧),各种模板套上感觉比以前做题时没有章法的乱做要好的多,效率要高的多。总体来说感觉是很不错的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值