寒假第一期学习内容与体会心得1

文章列举了一系列编程题目,涉及思维训练,如魔术球、粉刷栅栏、楚河汉界的小兵、走方格、铺砖、凸n形的不同划分方式(卡塔兰数)、破碎的项链、挤牛奶和雪地足迹等。这些题目主要考察贪心算法、动态规划、分治策略以及搜索技巧,旨在提高编程者解决问题的能力和优化解决方案的思维。
摘要由CSDN通过智能技术生成

Day 1:思维训练1-4
思维训练1重点
魔术球弱化版
粉刷栅栏
思维训练2重点
过了楚河汉界的小兵
走方格
铺砖2
凸n形的不同划分方式(卡塔兰数)
思维训练3重点
破碎的项链
挤牛奶
思维训练4重点
房屋积水(water.cpp)
膨胀的tyx
雪地足迹 Tracks in the Snow

1.魔术球弱化版
题目描述
假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为1,2,3…的球。
1.每次只能在某根柱子的最上面放球。
2.在同一根柱子中,任何2个相邻球的编号之和为完全平方数。
试设计一个算法,计算出在n根柱子上最多能放多少个球。例如,在4根柱子上最多可放11个球。
对于给定的 ,计算在n根柱子上最多能放多少个球。
输入格式
第1行有1个正整数n,表示柱子数。
输出格式
一行表示可以放的最大球数。
样例
样例输入

4

样例输出

11

数据范围与提示
对于所有的测试数据, N≤60

理解
这道题一看就是贪心,尽可能把球叠在一个柱子上。既然只是相邻组成平方数,那就没必要考虑球的编号。
直接上代码

#include<bits/stdc++.h>
int n,i,t=1,b=1;
int ls[105];

int pd(int x)
{
	for(int j=1;j*j<=x;j++){
		if(j*j==x) return 1;
	}
	return 0;
}

int main()
{
	scanf("%d",&n);
	while(b==1)
	{
		b=0;
		for(i=1;i<=n;i++){
			if(pd(t+ls[i])==1){
				ls[i]=t;
				t++;
				b=1;
				break; 
				}
			if(ls[i]==0){
				ls[i]=t;
				t++;
				b=1;
				break;
			}
		}
	}
	printf("%d",t-1);
	return 0;
}

2.粉刷栅栏
题目描述
小明家有一道栅栏,由N块竖立的木板构成,每块的宽度均为1,高度为H_i,相邻木板之间没有缝隙,紧密地连在一起形成一道栅栏墙。
栅栏需要刷漆,小明的刷子的宽度为1。我们把落下刷子连续刷动(中间不能提起刷子)直到提起刷子称为"一笔"。这与写字时的笔画数概念相同。‘’
小明可以竖着刷漆,一笔把一块木板从底到顶刚好刷完。他也可以横着刷漆,一笔把连续的若干块木板刷完一条宽度为1的范围,如果中间遇到高度上的缺口,则这一笔结束。刷过漆的部分允许重复刷漆。‘’
求:小明刷满整块栅栏墙最少需要多少笔。

输入格式
第1行:1个整数N,表示木板的数量
第2行:N个整数,分别表示每块木板的高度Hi
输出格式
第1行:1个整数,表示答案。

样例
样例输入

5
2 2 1 2 1

样例输出

3

数据范围与提示
数据范围:

1<=N<=50000
1<=Hi<=10^9

理解
这道题他不止是只横着刷或者竖着刷,而是考虑最优解的情况下刷。就是用分治做,先找最短的,初始次数为最短的,再去找次短的,看有没有连续,直到分段,再来新的区间。
上代码

#include <bits/stdc++.h>
using namespace std;
const int M=50005;
int n;
int ls[M];
int t(int l,int r)
{
	int min1=1e9+5;
	for(int i=l;i<=r;i++){
		min1=min(ls[i],min1);
	}
	int cnt=min1;
	for(int i=l;i<=r;i++)
	 {
		if(ls[i]==min1) continue;
		int j=i+1;
		while(ls[j]!=min1&&j<=r){
			j++;
		}
		j--;
		for(int m=i;m<=j;m++){
			ls[m]=ls[m]-min1;
		}
		cnt+=t(i,j); 
		i=j;
	 }
	 return min(cnt,r-l+1); 
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		cin>>ls[i];
	}
	printf("%d",t(1,n));
	return 0;
}

思维训练2
1.过了楚河汉界的小兵
题目描述
棋盘上A点有一个过河卒,需要走到目标B点。卒行走的规则:可以向下、或者向右。同时在棋盘上的任一点有一个对方的马(如C点),该马所在的点和所有跳跃一步可达的点称为对方马的控制点,如图3-1中的C点和P1,……,P8,卒不能通过对方马的控制点。棋盘用坐标表示,A点(0,0)、B点(n, m) (n,m为不超过20的整数),同样马的位置坐标是需要给出的,C≠A且C≠B。现在要求你计算出卒从A点能够到达B点的路径的条数。
在这里插入图片描述
输入格式
一行四个数据,分别表示B点坐标和马的坐标。
输出格式
一个数据,表示所有从A点能顺利到达B点的路径条数。
样例
样例输入

6 6 3 3

样例输出

6

理解
这个题因为是在棋盘上跳,所以需要用方向数组来判定,然后用前缀和来计算。
上代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
 
const int fx[]={0,-2,-1,1,2,2,1,-1,-2};
const int fy[]={0,1,2,2,1,-1,-2,-2,-1};
 
int bx,by,nx,my;
ll lj[40][40];
bool ls[40][40];
 
int main(){
	cin>>bx>>by>>nx>>my;
	bx+=2;
	by+=2;
	nx+=2;
	my+=2;
	lj[1][2]=1;
	ls[nx][my]=1;
	for(int i=1;i<=8;i++){
		ls[nx+fx[i]][my+fy[i]]=1;
	}
	for(int i=2;i<=bx;i++){
		for (int j=2;j<=by;j++){
			if(ls[i][j]) continue;
			lj[i][j]=lj[i-1][j]+lj[i][j-1];
		}
	}
	cout<<lj[bx][by]<<endl;
	return 0;
}

2.走方格
题目描述
有n×m的方格矩阵,小 A 从(1,1)出发到(n,m),只能向下或向右走,获得的分数为他经过方格的权值之和。

已知每个方格(i,j)的权值ai,j你可以将其中任意一个方格上的权值变为 0,求变化后小 A 最多能获得分数的最小值。

输入格式
第一行两个整数 n,m。
下面n行每行m个整数ai,j
输出格式
一个整数表示变化后小 A 最多能获得分数的最小值。
样例
样例输入 1

2 2
3 3
6 4

样例输出 1

9

样例解释 1
样例1: 将 (2,2)的权值变为0,路径为(1,1) ->(2,1)->(2,2),获得分数为3+6+0=9。
样例输入 2

3 3
1 1 1
2 1 2
3 1 1

样例输出 2

6

样例解释 2
样例2: 将 (2,1)的权值变成0,路径为(1,1)->(2,1)->(3,1)->(3,2)->(3,3),获得分数为1+0+3+1+1=6。
数据范围与提示
对于100%数据:1≤n,m≤2*10^3,1≤ai,j≤1000000000

理解

因为这题是求权值之和,所以应该用dp,先初始化后,就可以进行动态规划了。
废话不多说,直接上代码

#include <bits/stdc++.h>
using namespace std;

int n,m;
long long dp[2005][2005],dp1[2005][2005],ls[2005][2005],l[2005][2005],d[2005][2005];
long long mx=LONG_LONG_MAX;

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			scanf("%d",&ls[i][j]);
			dp[i][j]=max(dp[i-1][j],dp[i][j-1])+ls[i][j];
		}
	}
	for(int i=n;i>=1;i--){
		for(int j=m;j>=1;j--){
			dp1[i][j]=max(dp1[i+1][j],dp1[i][j+1])+ls[i][j];
		}
	}

	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			l[i][j]=max(dp[i][j-1]+dp1[i+1][j-1],l[i][j-1]);
			d[i][j]=max(dp[i-1][j]+dp1[i-1][j+1],d[i-1][j]); 
			mx=min(mx,max(max(l[i][j],d[i][j]),dp[i][j]+dp1[i][j]-ls[i][j]*2));
		}
	}
	cout<<mx<<endl;
	return 0;
}

3.铺砖2
题目描述
对于一个2行N列的走道。现在用1 * 2,2 * 2的砖去铺满。问有多少种不同的铺法?

输入格式
整个测试有多组数据,请做到文件结束。每行给出一个数字N,0≤N≤250
输出格式
输入多少行,输出就多少行
每行对应2*n的总铺法

样例
样例输入

2
8
12
100
200

样例输出

3
171
2731
845100400152152934331135470251
1071292029505993517027974728227441735014801995855195223534251

理解
这道题对比铺砖1来说本质上没变多少,但是有了更多的选法,自然也就麻烦一些,只不过要用前缀和,不然必定超时。
直接上代码

#include <bits/stdc++.h>
using namespace std;

int n,jw,len=1,tmp;
int ls[255][200];

int main(){
	ls[0][1]=1;
	ls[1][1]=1;
	ls[2][1]=3;
	ls[0][0]=ls[1][0]=ls[2][0]=1;
	for(int i=3;i<=250;i++){
		jw=0;
		for(int j=1;j<=len;j++){
			tmp=ls[i-1][j]+ls[i-2][j]*2+jw;
			ls[i][j]=tmp%10;
			jw=tmp/10;
		}
		if(jw){
			ls[i][++len]=jw%10;
			jw/=10;
		}
		ls[i][0]=len;
	}
	while(cin>>n){
		len=ls[n][0];
		for(int i=len;i>=1;i--) cout<<ls[n][i];
		cout<<endl;
	}
	return 0;
}

4.凸n形的不同划分方式(卡塔兰数)
题目描述
卡特兰数又称卡塔兰数,英文名Catalan number,是组合数学中一个常出现在各种计数问题中出现的数列。以比利时的数学家欧仁·查理·卡塔兰 (1814–1894)的名字来命名。
最初,给卡塔兰数建立的数学模型是:一个凸n边形,通过不相交于n边形内部的对角线,把n边形拆分成若干三角形,不同的拆分数目用hn表示,hn即为Catalan数。例如五边形有如下五种拆分方案(如图),故h5=5。求对于一个任意的凸n边形相应的hn。在这里插入图片描述
输入格式
一个正整数n,代表凸n边形的边数 (2≤n≤37)
输出格式
一个正整数,凸n边形划分成若干三角形的不同划分方式
样例
样例1输入

4

样例1输出

2

样例2输入

5

样例2输出

5

理解
题目描述也说了,这就是卡塔兰数(反正我是第一次知道卡塔兰数,可自行百度 ),所以可以用公式求。
上代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

LL ls[40]={0,0,1};
LL n;

int main(){
	cin>>n;
	for(int i=3;i<=n;i++){
		for(int j=2;j<=n-1;j++){
			ls[i]+=ls[j]*ls[i-j+1];
		}
	}
	ls[2]=0;
	cout<<ls[n]<<endl;
	return 0;
}

思维训练3
1.破碎的项链
题目描述
你有一条由N个红色的,白色的,或蓝色的珠子组成的项链(3<=N<=350),珠子是随意安排的。 这里是N=29 的二个例子:

第一和第二个珠子在图片中已经被作记号。 图片 A 中的项链可以用下面的字符串表示: brbrrrbbbrrrrrbrrbbrbbbbrrrrb

假如你要在一些位置断开项链,展开成一条直线,然后从一端开始收集颜色相同的珠子,直到你遇到一个不同颜色珠子。在另一端做同样的事(颜色可能与在这之前收集的不同)。两次收集的总和作为你在此处断开项链的收获。
确定应该在哪里断开项链能收集到最大数目的珠子。
举例来说,在图片 A 中的项链,可以收集到8个珠子:在珠子 9 和珠子 10之间断开, 或者在珠子 24 和珠子 25 之间断开项链。 在一些项链中,有一些白色的珠子如图片 B 所示。 当收集珠子的时候,遇到的白色珠子可以被当做红色也可以被当做蓝色。
表现项链的字符串将会包括三种字符: r , b 和 w 。r表示红色,b表示黑色,w表示白色。
写一个程序来确定一条项链最大可以收集的珠子数量。
输入格式
第 1 行: 一个整数N(3<=N<=350),表示珠子的数目
第 2 行: 一串长度为N的字符串, 每个字符是 r , b 或 w
输出格式
第1行:一个整数,表示可以收集的珠子数目的最大值。

样例
样例输入

29
wwwbbrwrbrbrrbrbrwrwwrbwrwrrb

样例输出

11

理解
这个项链是相连的,所以可以用三个自己连接一下,再来遍历设每一个都为断点,来求最大值。
上代码

#include <bits/stdc++.h>
using namespace std;

string s;
int n,mx=-1;

int f(int x){
	int cnt=0;
	char l=s[x],r=s[x+1];			//基准
	for(int i=x;;i--){
		if(s[i]==l)	cnt++;
		else if(s[i]=='w') cnt++;
		else break;	
	}
	for(int i=x+1;;i++){
		if(s[i]==r) cnt++;
		else if(s[i]=='w') cnt++;
		else break;
	}
	return cnt;

}

int main(){
	cin>>n>>s;
	s=s+s+s;
	for(int i=n;i<2*n;i++){
		if(s[i]==s[i+1]) continue;
		if(s[i]=='w'){
			s[i]='r';
			mx=max(f(i),mx);
			s[i]='b';
			mx=max(f(i),mx);
			s[i]='w';
		}
		mx=max(f(i),mx);
	}
	mx=min(mx,n);
	if(mx==-1) mx=n;
	cout<<mx<<endl;
	return 0;
}

2.挤牛奶
题目描述
三个农民每天清晨5点起床,然后去牛棚给3头牛挤奶。

第一个农民在300时刻(从5点开始计时,秒为单位)给他的牛挤奶,一直到1000时刻。

第二个农民在700时刻开始,在 1200时刻结束。

第三个农民在1500时刻开始2100时刻结束。

期间最长的至少有一个农民在挤奶的连续时间为900秒(从300时刻到1200时刻),而最长的无人挤奶的连续时间(从挤奶开始一直到挤奶结束)为300秒(从1200时刻到1500时刻)。

你的任务是编一个程序,读入一个有N个农民(1 <= N <= 5000)挤N头牛的工作时间列表,计算以下两个值(均以秒为单位):

  1. 最长至少有一人在挤奶的时间段。
  2. 最长的无人挤奶的时间段。

输入格式
第1行: 一个整数N(1<=N<=5000)。
第2…N+1行: 每行两个小于1000000的非负整数,表示一个农民的开始时刻与结束时刻。
输出格式
第1行:两个整数,即题目所要求的两个答案。

样例
样例输入

3
300 1000
700 1200
1500 2100

样例输出

900 300

理解
这道题就是不停地算左端点与右端点,并且不停地算最大值,最后就可以算出来了。
代码

#include <bits/stdc++.h>
using namespace std;

const int M=1000005;
int f[M];
int n,l,r,t,t1,s,s1,L=M,R;

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d%d",&l,&r);
		f[l]++;
		f[r]--;
		L=min(L,l);
		R=max(R,r);
	}
	for(int i=L;i<=R;){
		t=0;
		while(f[i]>0){
			t++;
			i++;
			f[i]=f[i]+f[i-1];
		}
		if(t>s) s=t;
		if(i==R) break;
		t1=0;
		while(f[i]==0 && i<=R){
			t1++;
			i++;
		}
		if(t1>s1) s1=t1;
	}
	printf("%d %d",s,s1);
	return 0;
}

思维训练4
1.房屋积水
题目描述
乌龟家的屋顶是凹凸不平的,所以每次雨后都会积水。为了知道屋顶是否会在暴雨后塌掉,他把屋顶的形状给了你,希望你帮他计算暴雨后屋顶的积水总量。
乌龟的屋顶由顺次排在同一水平线上的n个宽度为1、高度为整数 (分别给出) 的瓦片组成。例如给定n=5,瓦片的高度分别为4,2,3,5,1,屋顶可以画在下图所示的网格中,灰色格子为瓦片。
在这里插入图片描述暴雨过后,如果一个方格向左右两侧延伸都能到达瓦片占据的方格,它就会积水。所以图中波浪线格子在暴雨后会积水,屋顶的积水方格总数为 。

输入格式
第一行有一个正整数,表示屋顶的宽度。
第二行有个空格分开的整数,分别表示从左往右每一列瓦片的高度。
输出格式
一个整数,表示暴雨后屋顶积水方格的总数。

样例
样例输入:

5
4 2 3 5 1

样例输出:

3

数据范围与提示
数据范围:
1≤n≤10000
0≤hi<100000
理解
这道题就是把右端点和左端点的最大值算出来,然后求矮的位置,最后输出
代码

#include <bits/stdc++.h>
using namespace std;

int n,s;
int ls[10005],l[10005],r[10005];

int main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>ls[i];
	for(int i=1;i<=n;i++) l[i]=max(l[i-1],ls[i]);
	for(int i=n;i>=1;i--) r[i]=max(r[i+1],ls[i]);
	for(int i=1;i<=n;i++) s+=(min(l[i],r[i])-ls[i]);	
	cout<<s<<endl;
	return 0;
}
  1. 膨胀的tyx

题目描述
由于tyx抱过了这么多大腿,他变得越来越棒棒了,一般的迷宫根本拦不住他。为了治一治他,你决定为他单独定制一款无限迷宫。
所谓无限迷宫是指,由一个n×m的迷宫单元经过无限平铺得到的迷宫,即将无数份迷宫单元平铺在一个二维平面上。定制好以后,你将tyx扔到了迷宫里,不妨假定tyx落在了起点处。
出于对无限迷宫的恐惧,tyx想要尽可能逃离这里。那么问题来了,tyx能不能逃到距离起点无限远的地方去呢?

输入格式
第一行两个整数 ,用来描述迷宫单元的尺寸。

接下来是一个 的字符矩阵,用来描述这个迷宫,每个字符一定属于以下三种:

1.字符’.‘代表这个点是空地。
2.字符’S’代表这个点是起点。
3.字符’#'代表这个点是墙,不可以走。

输出格式
输出一行一个字符串“Yes”或“No”(不包括引号),“Yes”表示tyx可以逃到无限远的地方,“No”表示不可以。

样例
样例输入1

3 3
#.#
#.#
#S#

样例输出1

Yes

数据范围与提示
1≤N,M≤2000
理解

这道题主要是用广搜,判断四个方向是否可以走,并且用方向数组,以及用队列储存。
上代码

#include <bits/stdc++.h>
using namespace std;

int n,m,fx,fy;
char mp[2005][2005];
bool vis[2005][2005];
int ls[2005][2005][2];
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
struct node{
	int x,y,px,py;
}t1,t2;
queue<node>q;

void BFS(int X,int Y){
	t1.x=X;
	t1.y=Y;
	t1.px=X;
	t1.py=Y;
	ls[X][Y][0]=X;
	ls[X][Y][1]=Y;
	q.push(t1);
	while(!q.empty()){
		t1=q.front();
		q.pop();
		for(int i=0;i<4;i++){
			int dx=t1.x+dir[i][0];
			int dy=t1.y+dir[i][1];
			int tx=t1.px+dir[i][0];
			int ty=t1.py+dir[i][1];
			if(dx==n+1) dx=1;
			if(dx==0) dx=n;
			if(dy==m+1) dy=1;
			if(dy==0) dy=m;
			if(mp[dx][dy]!='#'){
				t2.px=tx;
				t2.py=ty;
				t2.x=dx;
				t2.y=dy;
				if(vis[t2.x][t2.y]){
					if(ls[dx][dy][0]!=tx || ls[dx][dy][1]!=ty){
						cout<<"Yes"<<endl;
						exit(0);
					}
					else continue;
				}
				vis[t2.x][t2.y]=1;
				ls[t2.x][t2.y][0]=tx,ls[t2.x][t2.y][1]=ty;
				q.push(t2);
			}
		}
	}	
}

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>mp[i][j];
			if(mp[i][j]=='S'){
				fx=i;
				fy=j;
			}	
		}
	}
	vis[fx][fy]=1;
	BFS(fx,fy);
	cout<<"No"<<endl;
	return 0;
}

3.雪地足迹 Tracks in the Snow
题目描述
在一片长方形的草地上,有 种动物——兔子和狐狸活动。兔子走过草地会留下 R,狐狸走过草地会留下 F。每只动物从左上角进入草地,从右下角走出草地。其间,它可以上下左右乱跳(可以重复),经过的格子会被覆盖上它的脚印。每次草地上最多只有一只动物。




RRR…
…RRR…
…R…
…RRRR.R
…RRR

FFR…
.FRRR…
.FFFFF…
…RRRFFR
…RRRFFR
…FFF
给你地图,问最少有多少只动物走过了草地。

输入格式
第一行:宽度H和高度W和(1≤H,W≤4000)
下面一个H×W的矩阵

输出格式
至少有多少只动物走过了草地。

样例
样例输入

5 8
FFR…
.FRRR…
.FFFFF…
…RRRFFR
…FFF

样例输出

2

样例解释

… RRR… FFR…
… …RRR… .FRRR…
… …R… .FFFFF…
… …RRRR.R …RRRFFR
… …RRR …FFF

数据范围与提示
对于30%的测试数据,N≤200,H,W≤500。 对于所有数据,1≤H,W≤4000。
理解
这道题因为是有两种动物,所以存的时候也需要两面存,正好符合deque。因为是最少所以要尽量让脚印连在一起,也就可以用deque。
直接上代码

#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> PR;

int n,m,cnt;
char mp[4005][4005];
int dir[4][2]={{-1,0},{0,1},{1,0},{0,-1}};
char last,cur;
deque <PR> dq;

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>mp[i]+1;
	last=char('R'+'F'-mp[1][1]);
	dq.push_front(PR(1,1));
	while(!dq.empty()){
		PR now=dq.front();
		dq.pop_front();
		cur=mp[now.first][now.second];
		if(cur==0) continue;
		mp[now.first][now.second]=0;
		if(cur!=last){
			last=cur;
			cnt++;
		}
		for(int i=0;i<4;i++){
			PR pos=PR(now.first+dir[i][0],now.second+dir[i][1]);
			char next=mp[pos.first][pos.second];
			if(next==cur) dq.push_front(pos);
			else if(next+cur=='R'+'F') dq.push_back(pos);
		}
	}
	cout<<cnt<<endl;
	return 0;
}

总结
虽然说了这么多,但也只是第一个板块,后面还有很多东西等着我讲,可编程就是这样一个难但有趣的过程,所以加油吧!!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值