21年下学期第四周周练

A—仙岛求药:

少年李逍遥的婶婶病了,王小虎介绍他去一趟仙灵岛,向仙女姐姐要仙丹救婶婶。叛逆但孝顺的李逍遥闯进了仙灵岛,克服了千险万难来到岛的中心,发现仙药摆在了迷阵的深处。迷阵由 M * N个方格组成,有的方格内有可以瞬秒李逍遥的怪物,而有的方格内则是安全。现在李逍遥想尽快找到仙药,显然他应避开有怪物的方格,并经过最少的方格,而且那里会有神秘人物等待着他。现在要求你来帮助他实现这个目标。

输入格式
第一行输入两个非零整数 M 和 N,两者均不大于 2020。M 表示迷阵行数, N 表示迷阵列数。

接下来有 M 行, 每行包含 N 个字符,不同字符分别代表不同含义:

  1. ‘@’:少年李逍遥所在的位置;2) ‘.’:可以安全通行的方格;3) ‘#’:有怪物的方格;4) ‘*’:仙药所在位置。

输出格式
输出一行,该行包含李逍遥找到仙药需要穿过的最少的方格数目(计数包括初始位置的方块)。如果他不可能找到仙药, 则输出 -1−1。

输出时每行末尾的多余空格,不影响答案正确性

样例输入
8 8
.@##…#
#…#.#
#.#.##…
…#.###.
#.#…#.
…###.#.
…#.*…
.#…###

样例输出
10


问题分析:

根据题目的要求,这是一道典型的BFS求最短路径的题目,这道题我使用了STL的队列来实现BFS,熟悉了STL的一些操作,以前都是自己写,有点麻烦,直接用真的有点爽。当然,除了队列,也可以使用别的方法来区别层数,比如对其做一个标记。


解决方案:

#include<bits/stdc++.h> 
using namespace std;
struct Node{
	int x,y,k;
};
char a[25][25];//地图 
int m,n;//行数,列数 
int U,V;//标记终点 
int c[4]={1,0,-1,0};
int d[4]={0,1,0,-1};
int ans;//记录步数 
int mask;//记录是否找到 
int main(){
	while(cin>>m>>n&&n&&m){
		queue<Node>Q;
		ans=INT_MAX;
		mask=0;
		for(int i=1;i<=m;i++){
			for(int j=1;j<=n;j++){
				cin>>a[i][j];
				if(a[i][j]=='@'){
					Node ss;
					ss.k=0;
					ss.x=i;
					ss.y=j;
					Q.push(ss);
				}
				else if(a[i][j]=='*'){
					U=i;
					V=j;
				}
			}
		}
	while(!Q.empty()&&!mask){
		Node s=Q.front();
		Q.pop();
		if(s.x==U&&s.y==V){
			ans=s.k;
			mask=1;
		}
		for(int i=0;i<4;i++){
			int AX=s.x+c[i];
			int AY=s.y+d[i];
			if(!mask&&AX<=m&&AX>=1&&AY<=n&&AY>=1&&a[AX][AY]!='#'){
				a[AX][AY]='#';
				Node ss;
				ss.k=s.k+1;
				ss.x=AX;
				ss.y=AY;
				Q.push(ss);		
			}
		}
	}
	if(ans==INT_MAX)cout<<"-1"<<endl;
	else cout<<ans<<endl;
	}
}

B—蛇形填数:

输入格式
直接输入方陈的维数,即 nn 的值。(n \le 100n≤100)

输出格式
输出结果是蛇形方阵。

输出时每行末尾的多余空格,不影响答案正确性

样例输入
3
样例输出
7 8 1
6 9 2
5 4 3


问题分析:

主要是对过程的模拟,确定在何时应该转向。


解决方案:

#include<bits/stdc++.h> 
using namespace std;
int a[105][105];
int n;
int main(){
	while(cin>>n){
		memset(a,0,sizeof(a));
		int count=1;
		int x=0,y=n-1;
		a[x][y]=1;
		while(count<n*n){
			while(x+1<n && !a[x+1][y]) a[++x][y]=++count;
        	while(y-1>=0 &&!a[x][y-1]) a[x][--y]=++count;
        	while(x-1>=0 &&!a[x-1][y]) a[--x][y]=++count;
        	while(y+1<n &&!a[x][y+1])  a[x][++y] =++count;
		}
		for(int i=0;i<n;i++){
			for(int j=0;j<n;j++){
				cout<<a[i][j]<<" ";
			}
			cout<<endl;
		}
	}
}

D—快递员送件:

有一个快递员要送件,配送站在节点 1。他总共要送 n−1 个物品,其目的地分别是节点 2 到节点 n。共有 m 条双向道路。这个邮递员每次只能带一样物品,并且运送每件物品过后必须返回配送站。求送完这 n−1 样东西并且最终回到配送站最少需要的时间。

Input
第一行包括两个整数,n 和 m,表示城市的节点数量和道路数量。 第二行到第 m+1 行,每行三个整数,u,v,w,表示从 u 到 v 有一条通过时间为 w 的道路。
Output
输出仅一行,包含一个整数,为最少需要的时间。

Sample Input
5 7
2 3 5
1 5 4
1 2 8
1 3 8
5 3 4
4 1 8
4 5 3

Sample Output
54


问题分析:

堆优化版Dijkstra


E—生日快乐:

windy的生日到了,为了庆祝生日,他的朋友们帮他买了一个边长分别为 X 和 Y 的矩形蛋糕。现在包括windy,一共有 N 个人来分这块大蛋糕,要求每个人必须获得相同面积的蛋糕。windy主刀,每一切只能平行于一块蛋糕的一边(任意一边),并且必须把这块蛋糕切成两块。这样,要切成 N 块蛋糕,windy必须切 N-1 次。为了使得每块蛋糕看起来漂亮,我们要求 N块蛋糕的长边与短边的比值的最大值最小。你能帮助windy求出这个比值么?

输入格式
  包含三个整数,X Y N。1 <= X,Y <= 10000 ; 1 <= N <= 10

输出格式
  包含一个浮点数,保留6位小数。

Sample Input
5 5 5
Sample Output
1.800000


问题分析:

用搜索,不断枚举出各种切法,并返回各自的最大比值,再从中得到最小的情况。
很暴力,很直接。


解决方案:

#include<bits/stdc++.h>
using namespace std;
const int maxn=10000;
int x,y,n;//n意味着要分为n块 
double Dfs(double x,double y,int n){
	//n=1,意味着已经无法再进行切割 
    if (n==1) return max(x,y)/min(x,y);
	double ans=INT_MAX;
	//先为横切一刀的影响,后为竖切一刀的影响 
    for (int i=1;i<n;i++) ans=min(ans,max(Dfs(x*i/n,y,i),Dfs(x*(n-i)/n,y,n-i)));
    for (int i=1;i<n;i++) ans=min(ans,max(Dfs(x,y*i/n,i),Dfs(x,y*(n-i)/n,n-i)));
    return ans;
}
int main()
{
    scanf("%d%d%d",&x,&y,&n);
     printf("%.6lf\n",Dfs(x,y,n));
}


F—非常男女:

近来,蒜头君致力于研究班上同学的配对问题(别想太多,仅是舞伴),通过各种推理和实验,他掌握了大量的实战经验。例如,据他观察,身高相近的人似乎比较合得来。

万圣节来临之际,蒜头君准备在学校策划一次大型的“非常男女”配对活动。对于这次活动的参与者,蒜头君有自己独特的选择方式。他希望能选择男女人数相等且身高都很接近的一些人。这种选择方式实现起来很简单。他让学校的所有人按照身高排成一排,然后从中选出连续的若干个人,使得这些人中男女人数相等。为了使活动更热闹,蒜头君当然希望他能选出的人越多越好。请编写程序告诉他,他最多可以选出多少人来。

输入格式
第一行有一个正整数
n(1≤n≤105),代表学校的人数。

第二行有 n个用空格隔开的数,这些数只能是 0 或 1,其中0 代表一个女生,1代表一个男生。

输出格式
输出一个非负整数,这个数表示在输入数据中最长的一段男女人数相等的连续子序列长度。

Sample Input
9
0 1 0 0 0 1 1 0 0

Sample Output
6

问题分析:

我一开始理解错了题,以为性别非男即女,岂不是直接输出人数最少的性别的二倍就行了,事实上我又忽略了顺序的重要,比如对于110000011,答案并不是8,而是4。可能是男女配对这四个字让人激动了
成功理解了题意,那么我们再看一下这道题,这道题的直接思路是用前缀和来实现,视女生为-1,男生为1,那么前缀和便能够反映这个点的性别情况, 同时还能根据相减得到两点之间的性别情况,当两个点的前缀和相同,则说明两个点之间男女个数相同,但前缀和具体实现又有三种方法:
1.双重循环查找
2.进行优化的双重循环查找
3.以空间换时间,在单次循环中达到目的:记录每种情况最早出现的点以及最后出现的点,相减即得到一个男女平衡的区间长度。注意,以女生为-1,男生为1,情况的可能范围是(-n,n),然而数组的下标不存在负数,于是乎就需要统一进行“+n”的操作,我们就需要开2*N的空间来放数组。
对于情况为n时,不需要进行标记,此时的n意味着性别相等,无影响,要保持统一;
对于没有出现的情况,其r-l为0,无影响;
对于只出现了一次的情况,仅标记左端点,r-l为负值,同样无影响;
对于出现两次及以上的情况,则有影响。


解决方案:

//能通过计蒜客测试,但没办法通过在洛谷的测试,会超时
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
int MAX;
int a[N];
int main(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		if(!a[i])a[i]=a[i-1]-1;
		else a[i]=a[i-1]+1;
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<i;j++){
			if(a[i]==a[j]){
				MAX=max(MAX,i-j);
			}
		}
	}
	cout<<MAX;
}
//进行了一波优化,对于第二重循环的检索范围进行控制,成功通过了洛谷
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
int MAX;
int a[N];
int main(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		if(!a[i])a[i]=a[i-1]-1;
		else a[i]=a[i-1]+1;
	}
	for(int i=1;i<=n;i++){
		for(int j=i+1+MAX;j<=n;j++){
			if(a[i-1]==a[j]){
				MAX=max(MAX,j-i+1);
			}
		}
	}
	cout<<MAX;
}
//以空间换时间
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
int MAX;
int a[N];
int l[2*N],r[2*N];
int main(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		if(a[i]==0)a[i]=a[i-1]-1;
		else a[i]=a[i-1]+1;
		int t=a[i]+n;
		if(!l[t]&&t!=n)l[t]=i;
		else r[t]=i;
	}
	for(int i=0;i<=2*n;i++){
		MAX=max(MAX,r[i]-l[i]);	
	}
	cout<<MAX;
}

G—激光炸弹 :

一种新型的激光炸弹,可以摧毁一个边长为R的正方形内的所有的目标。现在地图上有n(N<=10000)个目标,用整数Xi,Yi(其值在[0,5000])表示目标在地图上的位置,每个目标都有一个价值。激光炸弹的投放是通过卫星定位的,但其有一个缺点,就是其爆破范围,即那个边长为R的正方形的边必须和x,y轴平行。若目标位于爆破正方形的边上,该目标将不会被摧毁。

输入格式
输入文件的第一行为正整数n和正整数R,接下来的n行每行有3个正整数,分别表示xi,yi,vi

输出格式
输出文件仅有一个正整数,表示一颗炸弹最多能炸掉地图上总价值为多少的目标(结果不会超过32767)。

Sample Input
2 1
0 0 1
1 1 1

Sample Output
1

问题分析:

问题的关键在于快速求出一块正方形内的总价值和,(根据炸弹的位置,爆炸范围有可能为矩形,但是可以不考虑,因为一旦有矩形区域出现,必定存在一个正方形可以包括该矩形),也就是二维前缀和的运用。
注意:优化检索,炸弹的位置可以从[m][m]开始检索,同时可以在过程中维护N,M,记录范围。


解决方案:

#include<bits/stdc++.h> 
using namespace std;
int A[5005][5005];//自动往右下角移一位,默认从【1】【1】开始 
int n;//
int m;//范围 
int N,M;//记录实际范围 
int a,b,c;
int MAX;
int main(){
	cin>>n>>m;
	N=M=m;
	for(int i=0;i<n;i++){
		cin>>a>>b>>c;
		N=max(N,a+1);
		M=max(M,b+1);
		A[a+1][b+1]=c; 
	}
	for(int i=1;i<=N;i++){
		for(int j=1;j<=M;j++){
			A[i][j]=A[i][j]+A[i-1][j]+A[i][j-1]-A[i-1][j-1];
		}
	}
	for(int i=m;i<=N;i++){
		for(int j=m;j<=M;j++){
			MAX=max(A[i][j]+A[i-m][j-m]-A[i][j-m]-A[i-m][j],MAX);
		}
	}
	cout<<MAX;
}

H—小明与区间:

最近小明爱上了区间,对有关区间的一些问题很感兴趣,这天他得到这样一个问题:
给定一个数列,接下来对这个区间进行很多次询问与修改,询问是询问当前区间的最小值是几,修改是直接改变某个位置的数值,小明现在非常苦恼,聪明的你可以帮助小明解决这个问题吗?

Input
第一行,输入一个n,表示数列的长度 第二行包括 n 个数,表示数列 第三行输入一个q,表示询问的次数 接下来q行每行输入三个数 a,b,c 当a=0的时候,表示询问闭区间a到c的区间最小值 当a=1的时候,表示修改位置b处的值为c
Output
对于每一个a=0,输出答案,每个答案占一行。
Sample Input
10
618 5122 1923 8934 2518 6024 5406 1020 8291 2647
6
0 3 6
1 2 2009
0 2 2
0 2 9
1 1 5284
0 2 5

问题分析:

线段树模板

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值