A: 数字排序
题目描述
给定n个正整数,每个正整数均不超过10000,请编写一个程序统计每个整数出现的次数,并按照出现次数从多到少的顺序输出。
输入
单组输入,每组两行。
第1行包含一个正整数n,n<=1000,表示给定的正整数的个数。
第2行包含n个正整数,相邻两个正整数之间用一个英文空格隔开,表示待统计的整数。
输出
输出多行,每行包含两个正整数,分别表示一个给定的正整数和它出现的次数,按出现次数递减的顺序输出。
如果两个整数出现的次数一样多,则先输出值较小的整数,然后输出值较大的。
- 优先队列重载操作符
- 做法很多,我这不是最优解
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
int n;
map<int,int>mp;
struct node{
int x,y;
bool operator<(const node &q)const{
if(y!=q.y)return y<q.y;
else return x>q.x;
}
};
int main(){
scanf("%d",&n);
int d;
mp.clear();
for(int i=1;i<=n;i++){
scanf("%d",&d);
mp[d]++;
}
priority_queue<node>q;
while(!q.empty())q.pop();
map<int,int>::iterator it;
for(it=mp.begin();it!=mp.end();it++){
q.push({it->first,it->second});
}
while(!q.empty()){
printf("%d %d\n",q.top().x,q.top().y);
q.pop();
}
return 0;
}
B: X星人的数列
题目描述
爱冒险的X星人在一艘海底沉船上发现了一串神秘数列,这个数列的前6项如下:
0 1 3 7 15 31
X星人对这串数列产生了浓厚的兴趣,他希望你能够帮他发现这个神秘数列中所蕴含的规律,并且使用递归来编写一个程序输出该数列前N项的和。
当输入一个正整数N时,请输出这个神秘数列前N项的和。
输入
单组输入,每组输入一个正整数N(N<=20)。
输出
输出一个正整数,对应这个神秘数列前N项的和。
- 水题,
这好像是去年算法期末考试的一题 - 做法更多,随便做都能对
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
int fun(int d){
if(d==1)return 0;
else return 2*fun(d-1)+1;
}
int main(){
int n;
while(~scanf("%d",&n)){
ll ans=0;
for(int i=1;i<=n;i++)
ans+=fun(i);
printf("%lld\n",ans);
}
return 0;
}
C: 聪明的老鼠
题目描述
一只聪明的老鼠发现了一个三角形的奶酪迷宫。这个迷宫有若干个小房间,每个小房间有若干块大小相等的小奶酪(每个房间至少有一块奶酪)。
奶酪迷宫的构造如下:
(1) 奶酪迷宫一共有N行,第1行有一个小房间,第2行有两个房间,第3行有三个小房间,…第N行有N个小房间。
(2) 所有的小房间都是从第1列开始进行排列的。
(3) 奶酪迷宫的入口是第1行的那个小房间。
(4) 由于奶酪迷宫的特殊构造,小老鼠进入一个房间后,不允许回退到上一层的房间,也不允许走到左边的房间,只允许走到右边或者下面的房间。
(5) 在奶酪迷宫的最后一层,每个房间都有一扇通往迷宫出口的门,且最后一层的小房间没有通往左边和右边小房间的门。
现在小老鼠已经知道了每个小房间里面有多少块小奶酪,它找到了一条可以从入口走到出口且可以得到最多小奶酪的路径。
你能不能编写一个程序,输出小老鼠最多可以得到多少块小奶酪呢?
输入
单组输入。
第1行输入1个正整数N,表示奶酪迷宫的行数,N<=100。
接下来N行,第1行有1个正整数,表示第1行1个小房间的小奶酪数;第2行有2个正整数,表示第2行2个小房间的小奶酪数;…;第N行有N个正整数,表示第N行N个小房间的小奶酪数。每个房间的小奶酪数均不超过1000。每一行两个正整数之间用英文的空格隔开。
输出
输出小老鼠最多可以得到的小奶酪的数量。
- 动态规划
- 数字三角形变形题
#include<bits/stdc++.h>
using namespace std;
int n;
int dp[105][105];
int a[105][105];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
scanf("%d",&a[i][j]);
for(int i=1;i<=n;i++)
dp[n][i]=a[n][i];
for(int i=n-1;i>=1;i--)
for(int j=i;j>=1;j--)
dp[i][j]=max(dp[i+1][j],dp[i][j+1])+a[i][j];
printf("%d\n",dp[1][1]);
return 0;
}
D: 绿叶衬红花
题目描述
X星理工大学新学期开学典礼正在X星理工大学体育馆隆重举行,小小X作为新生代表坐上了主席台。
在无比骄傲的同时,看到下面坐着的黑压压的人群,小小X想到了这么一个问题:
作为一所理工大学,女生真的很少。俗话说,红花还需绿叶衬。假设所有参加开学典礼的同学坐成一个M行N列的矩阵,其中男生用“M”表示,女生用“F”表示。如果一个女生的前、后、左、右坐着另外一个女生,那么她们属于“同一朵红花”。
现在给出一个用于表示男生和女生就坐情况的字符矩阵,请编写一个程序统计在该字符矩阵中一共有多少朵“红花”?
输入
单组输入。
第1行输入两个正整数M和N,分别表示字符矩阵的行和列的数量(1<=M, N<=1000),二者之间用空格隔开。
第2行到第M+1行,每行N列,对应一个仅包含“M”和“F”两种字符的字符矩阵,表示男生和女生就坐情况。
输出
输出在字符矩阵中包含的“红花”数量。
- 搜索
- flood fill 模板题
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int n,m;
char mp[1005][1005];
int d[][2]={0,1,1,0,0,-1,-1,0};
int vis[1005][1005];
typedef pair<int,int>PI;
bool check(int x,int y){
if(x<1||y<1||x>n||y>m)return 0;
if(mp[x][y]!='F')return 0;
return 1;
}
void bfs(int x,int y){
queue<PI>q;
q.push({x,y});
while(!q.empty()){
pair<int,int>p=q.front();
q.pop();
if(vis[p.first][p.second])continue;
vis[p.first][p.second]=1;
for(int i=0;i<4;i++){
int dx=p.first+d[i][0];
int dy=p.second+d[i][1];
if(check(dx,dy))
q.push({dx,dy});
}
}
return ;
}
int main(){
scanf("%d %d",&n,&m);
getchar();
for(int i=1;i<=n;i++)
scanf("%s",mp[i]+1);
int ans=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(mp[i][j]=='F'&&!vis[i][j]){
bfs(i,j);
ans++;
}
printf("%d\n",ans);
return 0;
}
E: 破解密码
题目描述
X星人最近截取了Y星人的一段数字密文,这是一段长度为N(N>=2)的十进制正整数数字密文,且第1位是非零数。
聪明的X星人通过仔细分析和研究,终于找到了破解方法。破解过程非常复杂,包含若干步骤,其中第一步如下:
这段数字密文的第1位是一个密钥,设该密钥为K(K<N)。从这段数字密文中去掉K个数字后将得到一个新的正整数,其中所得到的最大正整数即为可用于进一步处理的中间码。
你能否编写一段程序帮助X星人将数字密文转换为对应的中间码?
输入
单组输入。
每组一行,输入一个长度为N的十进制正整数。(2<=N<=1000)
输出
输出数字密文所对应的中间码。
- 搜索
- bfs()搜索,优先队列重载操作符,优先搜索目前更大的字符串
#include <bits/stdc++.h>
#define ll long long
using namespace std;
string s;
int n;
struct node{
string str;
int ans;
bool operator<(const node &q)const
{
return str<q.str;
}
};
map<string,bool>vis;
void bfs(){
n=s[0]-'0';
priority_queue<node>q;
q.push({s,0});
while(!q.empty()){
node k=q.top();
q.pop();
if(vis[k.str])continue;
vis[k.str]=1;
if(k.ans==n){
cout<<k.str<<endl;
return ;
}
for(int i=0;i<k.str.size();i++){
string s1=k.str.substr(0,i)+k.str.substr(i+1);
q.push({s1,k.ans+1});
}
}
}
int main(){
cin>>s;
bfs();
return 0;
}
F: 防水板砖
题目描述
在X星的一片建筑工地上堆放着很多防水板砖,它们每一块的规格都一模一样,但是每一叠都高矮不一,这些砖块堆放得非常整齐而且非常紧凑。小XX数了一数,板砖一共有n行,m列。
现在给你一个n*m的矩阵,第n行m列上的数字表示对应位置的板砖数量,例如下面的矩阵所示:
2 3 5 1
4 1 2 3
1 5 4 2
1 2 2 2
第1行第1列的“2”表示这个位置对应的那一叠板砖的数量为2块。
某一天突然天降暴雨。暴雨过后,在板砖区形成了很多个小水坑。如果某一叠板砖的数量比它周围上、下、左、右的板砖数量少,将形成一个小水坑。相邻的两叠或者多叠板砖可能会构成一个大一点的水坑。例如在上面的图中,两叠红色的板砖将构成一个水坑,因为它们周围上、下、左、右的板砖(蓝色板砖)的数量比它们要多。
当然,最旁边的板砖是没有办法形成小水坑的。
假如这场雨下得足够大,足以让每一水坑都装满水。现在请问,暴雨过后在板砖区一共留下了多少个水坑?
输入
单组输入。
第1行输入两个正整数分别表示n和m,n和m均不超过100,两个数字之间用空格隔开。
接下来n行是一个n*m的矩阵,每一行m个正整数,表示某一叠板砖的数量,两个正整数之间用空格隔开。
输出
输出一个整数,即留下的水坑数量(存在一个水坑也没有的情况)。
- 搜索+思维
- 每个水坑中一定存在一块砖最矮,它比周围四块砖都要矮,并且边界的砖一定不可能成为水坑,那么我们就可以遍历里面的砖块,如果,这块砖比周围四个方向的砖都要矮,就可以开始从它开始搜索
- 那么搜到什么样的砖才可以继续搜索呢?
- 首先如果搜到边界肯定不行,并且这块砖要比除了搜索方向的其他三个方向的砖要矮,才能继续搜索
#include <bits/stdc++.h>
using namespace std;
int n,m;
int d[][2]={0,1,1,0,0,-1,-1,0};
int a[105][105];
bool vis[105][105];
int ans=0;
int check(int x,int y){
int num=0;
for(int i=0;i<4;i++){
int xx=x+d[i][0];
int yy=y+d[i][1];
if(a[xx][yy]>a[x][y]&&!vis[xx][yy])
num++;
}
return num;
}
void dfs(int x,int y){
if(vis[x][y])return ;
vis[x][y]=1;
for(int i=0;i<4;i++){
int xx=x+d[i][0];
int yy=y+d[i][1];
if(xx>1&&xx<n&&yy>1&&yy<m&&check(xx,yy)>=3){
dfs(xx,yy);
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
for(int i=2;i<n;i++)
for(int j=2;j<m;j++){
if(check(i,j)==4){
ans++;
dfs(i,j);
}
}
printf("%d\n",ans);
return 0;
}
G: X星人的选举
题目描述
一年一度的X星Boss选举大会即将开始。为了得到更多X星人的支持,候选人Mr. A决定在X星诸岛上举办巡回竞选演说。
已知X星上一共有N个小岛(N<=20),小岛的编号分别为1,2,3,…,N。有些岛之间有桥连接,有些岛之间没有桥,共有M座桥。这M座桥的信息(一座桥连接哪两个岛)均已给出。候选人只能通过这些桥来到达不同的小岛。
Mr. A决定从1号岛开始进行他的巡回演说。由于时间紧迫,他必须设计一条演说路线,从1号岛出发,经过其他每一个小岛一次,且只能经过一次,最后再回到1号岛。
请你编写一个程序来告诉Mr. A是否存在这样的路线?
输入
多组输入,第1行输入一个正整数T表示输入数据的组数。
针对每一组输入数据:
第1行输入两个正整数N和M,分别表示小岛的数量和桥的数量,(N<=20,M<=100),两个正整数之间用空格隔开。
接下来M行每一行包含两个正整数,表示一座桥连接的两个小岛的编号,两个正整数之间用空格隔开。
输出
针对每一组输入数据,如果存在满足要求的路线输出“Yes”,否则输出“No”。
- 图论
- 找是否存在汉密尔顿回路
- 我这直接用搜索的思路写的,一发过,看来没什么坑,
出题人很善良,要是我肯定挖几个坑,大家有空可以去做做OJ2125、2127、2130,我的原创题,闲来无事出着玩,保证很简单
#include<bits/stdc++.h>
using namespace std;
vector<int>g[105];
int n,m;
bool vis[105];
bool flag;
void dfs(int pos,int ans){
if(pos==1&&ans==n){//重新回到1点,并且已经走过了所有点
flag=1;
return ;
}
if(flag||(pos==1&&ans))return ;//已经找到回路,或者提前回到1点 return
for(int i=0;i<g[pos].size();i++){
if(!vis[g[pos][i]]){
vis[g[pos][i]]=1;
dfs(g[pos][i],ans+1);
vis[g[pos][i]]=0;
}
}
}
int main(){
int t;
scanf("%d",&t);
while(t--){
flag=0;
for(int i=0;i<=100;i++)g[i].clear();
scanf("%d%d",&n,&m);
memset(vis,0,sizeof(vis));
int a,b;
while(m--){
scanf("%d%d",&a,&b);
g[a].push_back(b);
g[b].push_back(a);
}
dfs(1,0);
if(flag)puts("Yes");
else puts("No");
}
return 0;
}
H: 小明的难题
题目描述
小明最近遇到了一个比较头疼的问题:
现在有一串无序的正整数,允许你修改其中一个数字,使得修改之后得到一个长度最长的单调递增子序列。例如:4 1 2 2 1 4 3。你可以将第4个数字“2”改为“3”(也可以将第5个数字“1”改为“3”),这样可以得到长度为4的单调递增子序列。(在单调递增子序列中后一个数字都要严格大于前一个数字)。
请你编写一个程序,计算一个序列修改其中某一个数字之后可以得到的最长单调递增子序列的长度。
输入
单组数据。
第1行输入序列中数字个数N。(N<=1000)
第2行输入N个正整数,两个正整数之间用空格隔开(1<=正整数<=1e8)。
输出
输出修改输入序列中某一个数字之后可以得到的最长单调递增子序列的长度。
- 看到题第一时间就想到最长上升子序列长度+1
- 他这说可以修改一个数,也没说不可以修改成小数,那我修改一个数,一定可以使最长上升子序列长度+1,除非长度就等于数组长度N
- 最长上升子序列长度的算法很多,这里给出n*logn的做法
这题个人感觉不是很严谨
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int n;
int a[1005];
int f[1005];
int main(){
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i];
int cnt=0;
f[cnt++] = a[0];
for (int i = 1; i < n; i++) {
if (a[i] > f[cnt-1]) f[cnt++] = a[i];
else {//二分查找
int l = 0, r = cnt - 1;
while (l < r) {
int mid = (l + r) >> 1;
if (f[mid] >=a[i]) r = mid;
else l = mid + 1;
}
f[r] = a[i];
}
}
cout<<min(n,cnt+1)<<endl;
return 0;
}
I: 简单迷宫题
题目描述
众多单机小游戏中都有王子救公主的情节,小波也脑补了一个迷宫游戏。现有一大小为nm二维迷宫,告诉你王子和公主的位置,小波觉得没有点实力是不配当王子的,于是王子每次可以向他的八个方向走一步(即:上、下、左、右、左上、左下、右上、右下)。迷宫中存在一些墙壁(用’#‘表示),同时还存在一些陷阱(用’‘表示,在这个游戏中,保证王子能够快速的发现陷阱并绕开,具体见样例),正常可以行走的路用’.'表示,王子的位置用’S’表示,公主的位置用’E’表示。现问你王子有多少条路可以营救公主(当然王子足够聪明,不会回头路)。
输入
第一行,迷宫的大小n、m(n,m<=100)。
接下来输入整个地图。
输出
一行,表示王子有多少种路线可以营救公主。
- 搜索
- 普通的搜索,就方向变为8个,找到‘E’路线+1就行
这种搜索做多了看完题都不用想,10分钟敲完
#include <bits/stdc++.h>
using namespace std;
int n,m;
char ch[105][105];
int ans;
int di[][2]={0,1,1,0,0,-1,-1,0,1,1,-1,-1,1,-1,-1,1};
int vi[105][105];
void dfs(int x,int y){
vi[x][y]=1;
if(ch[x][y]=='E'){
ans++;
return ;
}
for(int k=0;k<8;k++){
int xx=x+di[k][0];
int yy=y+di[k][1];
if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&!vi[xx][yy]&&ch[xx][yy]!='#'&&ch[xx][yy]!='*')
{
vi[xx][yy]=1;
dfs(xx,yy);
vi[xx][yy]=0;
}
}
return ;
}
int main(){
scanf("%d%d",&m,&n);{
memset(vi,0,sizeof(vi));
int x0,y0;
ans=0;
getchar();
for(int i=1;i<=n;i++)
{
scanf("%s",ch[i]+1);
for(int j=1;j<=m;j++)
if(ch[i][j]=='S')x0=i,y0=j;
}
dfs(x0,y0);
cout<<ans<<endl;
}
return 0;
}
J: 坠落的蚂蚁
题目描述
一根长度为1米的木棒上有若干只蚂蚁在爬动。它们的速度为每秒一厘米或静止不动,方向只有两种,向左或者向右。如果两只蚂蚁碰头,则它们立即交换速度并继续爬动。三只蚂蚁碰头,则两边的蚂蚁交换速度,中间的蚂蚁仍然静止。如果它们爬到了木棒的边缘(0或100厘米处)则会从木棒上坠落下去。在某一时刻蚂蚁的位置各不相同且均在整数厘米处(即1,2,3,…99厘米),有且只有一只蚂蚁A速度为0,其他蚂蚁均在向左或向右爬动。给出该时刻木棒上的所有蚂蚁位置和初始速度,找出蚂蚁A从此时刻到坠落所需要的时间。
输入
第一行包含一个整数表示蚂蚁的个数N(2<=N<=99),之后共有N行,每一行描述一只蚂蚁的初始状态。每个初始状态由两个整数组成,中间用空格隔开,第一个数字表示初始位置厘米数P(1<=P<=99),第二个数字表示初始方向,-1表示向左,1表示向右,0表示静止。
输出
蚂蚁A从开始到坠落的时间。若不会坠落,输出“Cannot fall!”。
- 思维+模拟
- 这里只给出我的做法和参考博客:坠落的蚂蚁
- 思路都差不多
#include <bits/stdc++.h>
using namespace std;
int n;
int pos;
int a,b;
int L[100],R[100];
int l,r;
struct node{
int x,y;//位置,速度
bool operator <(const node &q)const{
return x>q.x;
}
};
int main(){
cin>>n;
priority_queue<node>q;
while(n--){
cin>>a>>b;
q.push({a,b});
if(b==0)pos=a;
}
while(!q.empty()){
node k=q.top();
q.pop();
if(k.x<pos&&k.y==1){
L[l++]=k.x;
}
else if(k.x>pos&&k.y==-1){
R[r++]=k.x;
}
}
if(l==r)puts("Cannot fall!");
else if(l>r)printf("%d\n",100-L[l-r-1]);
else printf("%d\n",R[l]);
return 0;
}