链接: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(1≤n≤30)。
接下来 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 1≤n≤30。
一道搜索题,在比赛时是顺序思维,想到在按顺序读取时遇到的第一个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;
}
}
链接:[P2678 NOIP 2015 提高组] 跳石头 - 洛谷
一年一度的“跳石头”比赛又要开始了!
这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N N N 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。
为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 M M M 块岩石(不能移走起点和终点的岩石)。
输入格式
第一行包含三个整数 L , N , M L,N,M L,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。保证 L ≥ 1 L \geq 1 L≥1 且 N ≥ M ≥ 0 N \geq M \geq 0 N≥M≥0。
接下来 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 0≤M≤N≤10。
对于 50 % 50\% 50% 的数据, 0 ≤ M ≤ N ≤ 100 0 \le M \le N \le 100 0≤M≤N≤100。
对于 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 0≤M≤N≤50000,1≤L≤109。
又一次败到在了二分,比赛的时候没有想到,跑去结构体排序去了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;
}