3.20 补题(二分模板,反向搜索)

目录

  • D-填涂颜色(搜索)
    • 题目描述
    • 思路分析
    • 代码实现
  • F-跳石头(二分模板)
    • 题目描述
    • 思路分析
    • 代码实现

D-填涂颜色(搜索)

链接:P1162 填涂颜色 - 洛谷

题目描述

由数字 0 0 0 组成的方阵中,有一任意形状的由数字 1 1 1 构成的闭合圈。现要求把闭合圈内的所有空间都填写成 2 2 2。例如: 6 × 6 6\times 6 6×6 的方阵( n = 6 n=6 n=6),涂色前和涂色后的方阵如下:

如果从某个 0 0 0 出发,只向上下左右 4 4 4 个方向移动且仅经过其他 0 0 0 的情况下,无法到达方阵的边界,就认为这个 0 0 0 在闭合圈内。闭合圈不一定是环形的,可以是任意形状,但保证闭合圈内 0 0 0 是连通的(两两之间可以相互到达)。

0 0 0 0 0 0
0 0 0 1 1 1
0 1 1 0 0 1
1 1 0 0 0 1
1 0 0 1 0 1
1 1 1 1 1 1
0 0 0 0 0 0
0 0 0 1 1 1
0 1 1 2 2 1
1 1 2 2 2 1
1 2 2 1 2 1
1 1 1 1 1 1

输入格式

每组测试数据第一行一个整数 n ( 1 ≤ n ≤ 30 ) n(1 \le n \le 30) n(1n30)

接下来 n n n 行,由 0 0 0 1 1 1 组成的 n × n n \times n n×n 的方阵。

方阵内只有一个闭合圈,圈内至少有一个 0 0 0

输出格式

已经填好数字 2 2 2 的完整方阵。

输入 #1

6
0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 0 0 1
1 1 0 0 0 1
1 0 0 0 0 1
1 1 1 1 1 1

输出 #1

0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 2 2 1
1 1 2 2 2 1
1 2 2 2 2 1
1 1 1 1 1 1

说明/提示

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 30 1 \le n \le 30 1n30


思路分析

一道搜索题,在比赛时是顺序思维,想到在按顺序读取时遇到的第一个1的右下方一定是0,所以从右下方(i+1,j+1)开始搜索,且一个样例中只存在一个闭合圈所以只用搜索一次,这题在洛谷里的样例比较水,用这个思路也可以过(比赛时写的时候傻掉了XD,两层循环只跳了一层,所以挂掉了一个数据点qwq

赛后在学长的点拨下领悟到了正解!

分析题目,这是要将闭合的1里面的0改写成2,然后输出。

因此,猛然发觉,题目1的连通块相当于把0分为了2部分,一部分被围起也就是需要染色的,另一部分在外面不需要染色;

因为,从正面推,找闭合中的0不好找。所以,可以运用BFS或者DFS直接搜索圈外部的0然后标记。最后输出时,去除1点和标记了的点,剩下的输出为2,就是正解啦!!!

写的时候要注意从(1,1)开始输入,这样就可以直接从(0,0)开始搜索,搜索标记时判断到(n+1,n+1),防止1在边界的情况;(相当于在输入的图形外面又围了一圈0)


代码实现

这道题的样例很水所以就用DFS去写了,推荐用BFS去标记节省时间

(但是感觉DFS只要不重复进入时间应该是差不多的?)

#include
using namespace std;
#define int long long
#define endl '\n'
const int N=1e7;
int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
int a[35][35]; // 存图形
bool b[35][35]; // 标记不用染色的区域
int n;
void dfs(int x,int y){
	if(a[x][y]==1||b[x][y]==1) // 遇到边界1或已经被标记过
	return;
	b[x][y]=1; // 标记
	for(int i=0;i<4;i++){
		int tx=x+dx[i];
		int ty=y+dy[i];
		if(tx>=0&&ty>=0&&tx<=n+1&&ty<=n+1&&a[tx][ty]==0)
		dfs(tx,ty);
	}
}
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>a[i][j];
		}
	}
	dfs(0,0); 
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(b[i][j]==1) //如果被标记过,就说明在圈外,输出0
			cout<<"0"<<" ";
			else{
				if(a[i][j]==0) //在圈内的0变成2
				a[i][j]=2;
				cout<<a[i][j]<<" ";
			}
		}
		cout<<endl;
	}
} 

F-跳石头(二分模板)

链接:[P2678 NOIP 2015 提高组] 跳石头 - 洛谷

题目描述

一年一度的“跳石头”比赛又要开始了!

这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N N N 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。

为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 M M M 块岩石(不能移走起点和终点的岩石)。

输入格式

第一行包含三个整数 L , N , M L,N,M L,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。保证 L ≥ 1 L \geq 1 L1 N ≥ M ≥ 0 N \geq M \geq 0 NM0

接下来 N N N 行,每行一个整数,第 i i i 行的整数 D i   ( 0 < D i < L ) D_i\,( 0 < D_i < L) Di(0<Di<L), 表示第 i i i 块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。

输出格式

一个整数,即最短跳跃距离的最大值。

输入 #1

25 5 2 
2
11
14
17 
21

输出 #1

4

说明/提示

输入输出样例 1 说明

将与起点距离为 2 2 2 14 14 14 的两个岩石移走后,最短的跳跃距离为 4 4 4(从与起点距离 17 17 17 的岩石跳到距离 21 21 21 的岩石,或者从距离 21 21 21 的岩石跳到终点)。

数据规模与约定

对于 20 % 20\% 20%的数据, 0 ≤ M ≤ N ≤ 10 0 \le M \le N \le 10 0MN10
对于 50 % 50\% 50% 的数据, 0 ≤ M ≤ N ≤ 100 0 \le M \le N \le 100 0MN100
对于 100 % 100\% 100% 的数据, 0 ≤ M ≤ N ≤ 50000 , 1 ≤ L ≤ 1 0 9 0 \le M \le N \le 50000,1 \le L \le 10^9 0MN50000,1L109


思路分析

又一次败到在了二分,比赛的时候没有想到,跑去结构体排序去了QAQ

既然已经确定是二分去做了,那先来上个模板?

解题的时候往往会考虑枚举答案然后检验枚举的值是否正确。若满足单调性,则满足使用二分法的条件。就可以把枚举换成二分,就变成了「二分答案」。

while(l<r){
		mid=l+r+1>>1;
		if(pjdr(mid))
		l=mid;
		else
		r=mid-1;
	}

接着就是去写判断函数了

二分答案最小的距离,遍历数组,

如果当前的距离小于mid那就搬走这个石头,计数器+1;

如果距离大了那这块石头就不能动,更新p的位置;

遍历结束后,如果计数器an(需要搬走石头的数量)大于m(题目中给的最大的能移动的数量)则不符合题意,缩小右边界;反之满足题意就更新左边界;

bool pjdr(int mid){
	int p=0,an=0;
	for(int i=1;i<=n;i++){ // 用p去记录上一个石头的位置
		if(a[i]-p<mid) //如果距离小于mid,计数器+1
		an++; 		   //(相当于搬走了这块石头)
		else //大于mid就更新p
		p=a[i];
	}
	if(an<=m) return 1; //需要搬走的石头小于等于题目要求的,就返回1
	else return 0;
}

代码实现

#include
using namespace std;
#define int long long
#define endl '\n'
const int N=1e7;
int a[N];
int s,n,m;
bool pjdr(int mid){
	int p=0,an=0;
	for(int i=1;i<=n;i++){
		if(a[i]-p<mid)
		an++;
		else
		p=a[i];
	}
	if(an<=m) return 1;
	else return 0;
}
signed main(){
	cin>>s>>n>>m;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	a[++n]=s;
	int l=0,r=s,mid;
	while(l<r){
		mid=l+r+1>>1;
		if(pjdr(mid))
		l=mid;
		else
		r=mid-1;
	}
	cout<<l<<endl;
} 

你可能感兴趣的:(深度优先,算法,图论,c++)