window.cnblogsConfig = {//可以放多张照片,应该是在每一个博文上面的图片,如果是多张的话,那么就随机换的。 homeTopImg: [ "https://cdn.luogu.com.cn/upload/image_hosting/clcd8ydf.png", "https://cdn.luogu.com.cn/upload/image_hosting/clcd8ydf.png" ], }

CF31D解题报告

题意

给你一个长 \(W\)\(H\) 的矩形,其中左下角为 \((0,0)\),右上角为 \((W,H)\)

之后给你一个 \(n\) 表示切多少刀,每一刀给出起始坐标和结束坐标,不一定从头切到尾

分析

先说一下,这里可以看作一个个方格连接起来,下面说的坐标是方格的,而每一刀的坐标是边上的,需要转换一下把边转到方格上。可以自行画图理解。

先来看每一刀,针对这个数据范围,可以直接去标记被切的那一列或行附近的方格不能朝这里走。具体的,定义一个三维数组 \(vis1_{i,x,y}\),第一维表示方向,第二、三维表示坐标,如果为 \(1\) 那么 \((x,y)\) 不能走 \(i\) 方向(和方向数组对应)。

接下来核心部分,考虑到要求每一部分的大小,并且与棋盘图上的搜索类似,可以直接当连通块来做,具体不在此赘述。最后排序输出。

注意,一条边会导致两个方向不能走过来,\(vis1\) 要在两个方向进行更新,注意两点坐标差异。

代码

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

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0};
int w, h, n, cnt, len;
int x1, y1, x2, y2;
int vis[105][105], vis1[4][105][105];
vector<int> v;

void dfs(int x, int y){
	cnt++;
	vis[x][y] = 1;
	for (int i = 0; i < 4; i++){
		int nx = x + dx[i], ny = y + dy[i];
		if (nx > 0 && nx <= w && ny > 0 && ny <= h && vis[nx][ny] == 0 && vis1[i][x][y] == 0) dfs(nx, ny);
	}
}

int main(){
	scanf("%d%d%d", &w, &h, &n);
	for (int i = 1; i <= n; i++){
		scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
		if (x1 == x2){//处理行
			for (int j = y1 + 1; j <= y2; j++) vis1[1][x1][j] = vis1[3][x1 + 1][j] = 1;//边转方格
		}
		else{//处理列
			for (int j = x1 + 1; j <= x2; j++) vis1[0][j][y1] = vis1[2][j][y1 + 1] = 1;
		}
	}
	for (int i = 1; i <= w; i++){
		for (int j = 1; j <= h; j++){
			if (vis[i][j] == 0){
				cnt = 0;//连通块大小
				dfs(i, j);//标记连通块
				v.push_back(cnt);
			}
		}
	} 
	sort(v.begin(), v.end());//排序输出
	for (int i = 0; i < v.size(); i++) printf("%d ", v[i]);
	return 0;
}
posted @ 2024-01-18 07:47  CCF_IOI  阅读(23)  评论(0)    收藏  举报