蓝桥杯 剪格子+错误票据+连号区间数

本文介绍了蓝桥杯竞赛中的三个算法题目:剪格子问题,要求判断一个矩阵是否能分割成两个和相等的部分;错误票据问题,需要找出连续ID中的断号和重号;连号区间数问题,求解全排列中的连号区间个数。针对每个问题,提供了题目描述、输入输出格式和解题思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

剪格子

题目:

问题描述
如下图所示,3 x 3 的格子中填写了一些整数。
+--*--+--+
|10* 1|52|
+--****--+
|20|30* 1|
*******--+
| 1| 2| 3|
+--+--+--+
我们沿着图中的星号线剪开,得到两个部分,每个部分的数字和都是60。

本题的要求就是请你编程判定:对给定的m x n 的格子中的整数,是否可以分割为两个部分,使得这两个区域的数字和相等。

如果存在多种解答,请输出包含左上角格子的那个区域包含的格子的最小数目。

如果无法分割,则输出 0。

输入格式
程序先读入两个整数 m n 用空格分割 (m,n<10)。

表示表格的宽度和高度。

接下来是n行,每行m个正整数,用空格分开。每个整数不大于10000。

输出格式
输出一个整数,表示在所有解中,包含左上角的分割区可能包含的最小的格子数目。
样例输入1
3 3
10 1 52
20 30 1
1 2 3
样例输出1
3
样例输入2
4 3
1 1 1 1
1 30 80 2
1 1 1 100
样例输出2
10

思路:

因为要包括左上角第一个格子,我们就可以从左上的第一个格子开始搜索,搜索包含左上角所有满足将格子分成两个和相等的可能,即所有从第一个格子出发,且格子中数字之和等于half_sum的尊和。同时,在搜索过程中,用min_grid记录满足格子中数字和为所有格子中数字和且格子数较少的格子数量。当搜索完成后,min_grid中存的就是满足题意的最小格子数。

优化操作:在进入搜索之前可以提前判断一下格子中数字之和(sum)是否为奇数,如果为奇数则无法分成相等的两格子;其次,如果格子中最大的数已经大于总和(sum)的一半了,也无法分成相等的两半

#include<iostream>

using namespace std;
int a[10][10]={0};//用来存放每个格子的数
int visit[10][10] = {0};//记录每个格子是否被访问  
int dx[]={-1,1,0,0};//方向数组
int dy[]={0,0,-1,1}; 
int _sum = 0;//用来存储遍历过的格子中数的和
int min_grid = 100;//最多100个格子
int count=0;//计每次的格子数 
int m = 0;
int n = 0;
void dfs(int value,int x,int y,int &target){
	visit[x][y]=1;
	count += 1;
	 _sum += value;
	if(_sum==target){
		if(min_grid > count)
			min_grid = count;	
	}
	else{
		for(int i =0;i<4;i++){
			//遍历四个方向
			int new_x = x+dx[i]; 
			int new_y = y+dy[i]; 
			if(new_x<0||new_y<0||new_x>n||new_y>m
				||visit[new_x][new_y]==1||(_sum+a[new_x][new_y])>target)//超出边界、访问过或者_sum加上新坐标的值大于target则遍历下一个方向 
				continue;
			else{
				dfs(a[new_x][new_y],new_x,new_y,target);
			}
		}
	}
	//回溯
	count--;
	_sum -= value;
	visit[x][y]=0;
}
int main(){

	int max_num=0;//存储最大数,最大数如果大于和的一半则不能分成两半相等的格子
	int all_sum=0;//用来存储所有元素的和 
	cin>>m>>n;
	for(int i=0;i<n;i++){//初始化a 
		for(int j=0;j<m;j++){
			cin>>a[i][j];
			all_sum+=a[i][j];
			if(a[i][j]>max_num)
				max_num = a[i][j]; 
		}		
	}
	int half_sum = all_sum/2;
	if(max_num > half_sum||all_sum%2!=0)
		cout<<0;
	else{
		dfs(a[0][0],0,0,half_sum);
		if(min_grid!=100)//说明找到了相应组合
			cout<<min_grid;
		else  //没找到
			cout<<0;
	}
	cout<<endl;
	return 0;
} 

错误票据

题目:

问题描述

某涉密单位下发了某种票据,并要在年终全部收回。

每张票据有唯一的ID号。全年所有票据的ID号是连续的,但ID的开始数码是随机选定的。

因为工作人员疏忽,在录入ID号的时候发生了一处错误,造成了某个ID断号,另外一个ID重号。

你的任务是通过编程,找出断号的ID和重号的ID。

假设断号不可能发生在最大和最小号。

输入格式

要求程序首先输入一个整数N(N<100)表示后面数据行数。

接着读入N行数据。

每行数据长度不等,是用空格分开的若干个(不大于100个)正整数(不大于100000),请注意行内和行末可能有多余的空格,你的程序需要能处理这些空格。

每个整数代表一个ID号。

输出格式

要求程序输出1行,含两个整数m n,用空格分隔。

其中,m表示断号ID,n表示重号ID

样例输入1

2
5 6 8 11 9 
10 12 9

样例输出1

7 9

样例输入2

6
164 178 108 109 180 155 141 159 104 182 179 118 137 184 115 124 125 129 168 196
172 189 127 107 112 192 103 131 133 169 158 
128 102 110 148 139 157 140 195 197
185 152 135 106 123 173 122 136 174 191 145 116 151 143 175 120 161 134 162 190
149 138 142 146 199 126 165 156 153 193 144 166 170 121 171 132 101 194 187 188
113 130 176 154 177 120 117 150 114 183 186 181 100 163 160 167 147 198 111 119

样例输出2

105 120

思路:这道题其实比较简单,将输入的数放进vector中,排序后循环遍历数组中的所有数,如果前后两个相差0则说明是重号,将两个数中任意一个数赋给n就可以了,如果两个数相差为2则说明有断号,nums[i]+1即为断号。不太明白的是,写的代码必须输入EOF才会停止输入,严格来说并不符合题意,但是提交却通过了??看了一些其他的代码也基本存在一样的问题,算了,也不深究了

#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

int N;
vector<int> nums;//存放票据号

int main(){
	int temp;//每个输入的票据号 
	cin>>N;
	int m=0;//断号 
	int n=0;//重号 
	while(cin>>temp){//将输入的票据号存入nums中 
		nums.push_back(temp);
	} 
	sort(nums.begin(),nums.end());//将nums中的数据按从小到大的顺序排列
	for(int i=0;i<nums.size()-1;i++){
		if(nums[i+1]-nums[i]==0)//后一个与前一个相等为重号 
			n = nums[i];
		if(nums[i+1]-nums[i]==2)//后一个数字与前一个数字差2为断号 
			m = nums[i]+1; 
	} 
	cout<<m<<' '<<n<<endl;
	return 0; 
} 

连号区间数:

题目:

问题描述

小明这些天一直在思考这样一个奇怪而有趣的问题:

在1~N的某个全排列中有多少个连号区间呢?这里所说的连号区间的定义是:

如果区间[L, R] 里的所有元素(即此排列的第L个到第R个元素)递增排序后能得到一个长度为R-L+1的“连续”数列,则称这个区间连号区间。

当N很小的时候,小明可以很快地算出答案,但是当N变大的时候,问题就不是那么简单了,现在小明需要你的帮助。

输入格式

第一行是一个正整数N (1 <= N <= 50000), 表示全排列的规模。

第二行是N个不同的数字Pi(1 <= Pi <= N), 表示这N个数字的某一全排列。

输出格式

输出一个整数,表示不同连号区间的数目。

样例输入1

4
3 2 4 1

样例输出1

7

样例输入2

5
3 4 2 5 1

样例输出2

9

分析:按照题目要求,分析发现要想某一区间的数字排序后为连续数列,那么在该区间的数字排序后应为相邻数字,因此区间中最大最小的数字相减应该正好等于该区间下标之差;在考虑了上诉两个及以上的组合情况后不要忘了每个数字单独也是符合题意的!!

#include<iostream>
#include<vector>

int N;
using namespace std;

vector<int> nums;//全排列数

int max_num;//某区间的最大数 
int min_num;//某区间的最小数 
int _count;//统计个数 
int main(){
	int temp;
	cin>>N; 
	while(N--){
		cin>>temp;
		nums.push_back(temp);	
	}
	N = nums.size();
	//寻找所有两个及以上元素的连号区间
	for(int i=0;i < nums.size();i++){
		max_num = nums[i];
		min_num = nums[i];
		for(int j=i+1;j<nums.size();j++){
			if(nums[j] > max_num)
				max_num = nums[j];
			if(nums[j] < min_num)
				min_num = nums[j];
			if((max_num-min_num)==(j-i))
				_count+=1; 
		} 
	}
	_count+=N;
	cout<<_count<<endl;//最后还要考虑单一元素的可能
	return 0; 
} 

!!!与系统重名会出现变量指代不明的的错误。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值