UVA12171ac心得

2 篇文章 0 订阅

首先要注意看懂题目的要求,求体积时,如果有空腔(也就是周围都是实心,中间是空气),也要计入体积,

所以相当于先在外围构造一圈空气,然后用总体积减去外围空气的总体积即可.

第二计算雕塑表面积的时候,实际上就是计算和外围空气有接触的那些面的面积,具体的说,假定从为空气的单元格出发,

走到它的一个邻居时发现邻居为实心,此时应该计算的是源单元格的那个接触面的面积(因为进行离散化处理以后,目标实心格的那一面不一定都和空气接触),这个接触面的面积要根据行走方向来确定.

3,所谓离散化,就是先根据所有实心长方体的两个对角点信息求出所有不同的x,y,z坐标的列表,为了方便计算,每个坐标列表可以再增加一个最小值和和最大值,

然后整个区域把这些不同的区间进行划分,比如离散化之前整个区域有

10*10*10个单元格,然后不同的x坐标有4个,y坐标有5个,z坐标有6个,那么离散化之后要处理的区域数就是(4-1)*(5-1)*(6-1)=60个,

相当于把离散化之前的很多相邻的空心小单元格合并到一起了,加快了处理的效率

4.离散化以后的每个区域只需要知道其左下坐标即可,准确的说是坐标在列表中的编号,离散化的一个好处就是,假定某个区域的左下角的坐标索引为(x,y,z),那么这个区域的对角点坐标索引一定为(x+1,y+1,z+1),这样就能迅速计算出这个区域的体积和各个面的面积.

下面是ac代码

/*
本代码的思路主要参考了课本原文以及
https://blog.csdn.net/hm970928/article/details/78068515
*/
#include <iostream>
#include <iomanip>
#include <cassert>
#include <list>
#include <cctype>
#include <climits>
#include <algorithm>
#include <map>
#include <set>
#include <queue>
#include <iterator>
#include <stack>
#include <string>
#include <vector>
#include <functional>
#include <utility>
#include <sstream>
#ifdef _DEBUG
#include <fstream>
#endif // _DEBUG
using namespace std;
#ifdef _DEBUG
ifstream ifs;
ofstream ofs;

const int UVA_PROBLEM_NO = 12171;

void redirect_input()
{
	ostringstream oss;
	oss << "uva" << UVA_PROBLEM_NO << "_in.txt";
	ifs.open(oss.str().c_str());
	cin.rdbuf(ifs.rdbuf());
}

void redirect_output()
{
	ostringstream oss;
	oss << "uva" << UVA_PROBLEM_NO << "_out.txt";
	ofs.open(oss.str().c_str());
	cout.rdbuf(ofs.rdbuf());
}

#endif

void main_proc();
int main()
{
#ifdef _DEBUG
	redirect_input();
	redirect_output();
#endif // _DEBUG

	main_proc();

#ifdef _DEBUG
	ifs.close();
	ofs.close();
#endif
	return 0;
}

const bool EMPTY = false;
const bool SOLID = true;

//3维空间中一个节点可能移动的6个方向
const int X_POS = 0;
const int X_NEG = 1;
const int Y_POS = 2;
const int Y_NEG = 3;
const int Z_POS = 4;
const int Z_NEG = 5;

//沿这6个方向移动时,x y z的坐标差
const int dx[] = { 1,-1,0,0,0,0 };
const int dy[] = { 0,0,1,-1,0,0 };
const int dz[] = { 0,0,0,0,1,-1 };

//记录长方体的两个对角点的坐标
struct Box {
	int x1;
	int y1;
	int z1;
	int x2;
	int y2;
	int z2;

	void read()
	{
		cin >> x1 >> y1 >> z1;
		int xL, yL, zL;
		cin >> xL >> yL >> zL;
		x2 = x1 + xL;
		y2 = y1 + yL;
		z2 = z1 + zL;
	}
};

//保存某个网格左下角的坐标索引
struct Cell {
	int xindex;
	int yindex;
	int zindex;

	Cell(int xindex = 0, int yindex = 0, int zindex = 0)
		:xindex(xindex), yindex(yindex), zindex(zindex)
	{

	}

	void move(int dir) {
		xindex += dx[dir];
		yindex += dy[dir];
		zindex += dz[dir];
	}

	Cell (const Cell&other) 
		:xindex(other.xindex)
		,yindex(other.yindex)
		,zindex(other.zindex)
	{

	}

	Cell& operator=(const Cell&other) {
		if (this != &other)
		{
			xindex = other.xindex;
			yindex = other.yindex;
			zindex = other.zindex;
		}
		return *this;
	}
	
	void print()const {
		cout << "(" << xindex
			<< "," << yindex
			<< "," << zindex
			<< ")"
			;
	}
};



//把集合里的值依次存到数组里
void convert_to_vector(const set<int>& values, vector<int>&v, map<int, int> & indexes)
{
	int min_value = *(values.begin()) - 1;
	int max_value = *(values.rbegin()) + 1;
	//int min_value = 0;
	//int max_value = 1001;
	size_t L = values.size()+2;
	v.resize(L);
	v[0] = min_value;
	indexes[min_value] = 0;
	v[L - 1] = max_value;
	indexes[max_value] = L - 1;
	int i = 1;
	for (auto & e:values)
	{
		v[i] = e;
		indexes[e] = i;
		++i;
	}
	
}

struct Sculpture {
	vector<int> x_cors;  //x坐标列表
	vector<int> y_cors;  //y坐标列表
	vector<int> z_cors;  //z坐标列表

	vector<vector<vector<bool> > >  grid_color; //网格列表,格子的总数=x坐标的个数*y坐标的个数*z坐标的个数
	                                            //对应下标为x y z坐标在各自坐标列表中的下标(都是左下角),值代表颜色
	
	
	int total_volume; //整个区域的总面积
	int area;         //雕塑的总表面积
	int empty_volume; //外围空气的总体积

	int get_volume(const Cell& cell)const {
		return get_volume(cell.xindex, cell.yindex, cell.zindex);
	}

	//计算某个网格的体积
	int get_volume(int xindex, int yindex, int zindex)const {
		int xL = x_cors[xindex + 1] - x_cors[xindex];
		int yL = y_cors[yindex + 1] - y_cors[yindex];
		int zL = z_cors[zindex + 1] - z_cors[zindex];

#if 0
		if (xindex == 1 && yindex == 1 && zindex == 0)
		{
			cout << endl;
			cout << "xL=" << x_cors[xindex + 1] <<"-"<< x_cors[xindex] 
				<<"="<< xL << endl;
			cout << "yL=" << y_cors[yindex + 1] << "-" << y_cors[yindex]
				<< "=" << yL << endl;
			cout << "zL=" << z_cors[zindex + 1] << "-" << z_cors[zindex]
				<< "=" << zL << endl;
		}
#endif 
		return xL * yL * zL;
	}

	int get_area(const Cell& cell, int dir) {
		return get_area(cell.xindex, cell.yindex, cell.zindex, dir);
	}

	/*沿方向dir遇到左下坐标为(x,y,z)的网格时,计算接触面的面积
	比如如果是沿x轴方向接触到这个格子时,那么表面积就是yL*zL,另外两个方向类似
	*/
	int get_area(int xindex, int yindex, int zindex, int dir)const 
	{
		int area = 0;
		int xL = x_cors[xindex + 1] - x_cors[xindex];
		int yL = y_cors[yindex + 1] - y_cors[yindex];
		int zL = z_cors[zindex + 1] - z_cors[zindex];
		switch (dir)
		{
		case X_POS:
		case X_NEG:
			area = yL * zL;
			break;
		case Y_POS:
		case Y_NEG:
			area = xL * zL;
			break;
		case Z_POS:
		case Z_NEG:
			area = xL * yL;
				break;
		default:
			break;
		}
		return area;
	}


	void init(const vector<Box>& boxes) {
		set<int> sx;
		set<int> sy;
		set<int> sz;
		for (auto &e: boxes){
			sx.insert(e.x1);
			sx.insert(e.x2);
			sy.insert(e.y1);
			sy.insert(e.y2);
			sz.insert(e.z1);
			sz.insert(e.z2);
		}

		map<int, int> x_indexes; //每个x坐标在列表中的下标
		map<int, int> y_indexes; //每个y坐标在列表中的下标
		map<int, int> z_indexes; //每个z坐标在列表中的下标

		convert_to_vector(sx, x_cors, x_indexes);
		convert_to_vector(sy, y_cors, y_indexes);
		convert_to_vector(sz, z_cors, z_indexes);

		size_t nx = x_cors.size();
		size_t ny = y_cors.size();
		size_t nz = z_cors.size();

		//首先把所有网格都设为空气
		grid_color = vector<vector<vector<bool> > >(nx,
			vector<vector<bool> >(ny, vector<bool>(nz, EMPTY)));

		//把每个长方体内部含有的所有网格的颜色都设为实心
		for (auto &e : boxes) {
			int start_xindex = x_indexes[e.x1];
			int end_xindex = x_indexes[e.x2];
			int start_yindex = y_indexes[e.y1];
			int end_yindex = y_indexes[e.y2];
			int start_zindex = z_indexes[e.z1];
			int end_zindex = z_indexes[e.z2];
			for (int i=start_xindex;i<end_xindex;++i)
				for (int j = start_yindex;j<end_yindex;++j)
					for (int k = start_zindex;k<end_zindex;++k)
			{
					grid_color[i][j][k] = SOLID;
			}
		}
	}

	//计算整个区域的面积
	int get_total_volume()const{
		int xL = x_cors.back() - x_cors[0];
		int yL = y_cors.back() - y_cors[0];
		int zL = z_cors.back() - z_cors[0];
		return xL * yL * zL;
	}

	bool in_side(const Cell& cell)const {
		return in_side(cell.xindex, cell.yindex, cell.zindex);
	}

	bool in_side(int xindex, int yindex, int zindex)const
	{
		size_t nx = x_cors.size();
		size_t ny = y_cors.size();
		size_t nz = z_cors.size();

		return (xindex >= 0 && xindex + 1 < nx)
			&& (yindex >= 0 && yindex + 1 < ny)
			&& (zindex >= 0 && zindex + 1 < nz);
	}

	//计算空气的总体积和雕塑的总表面积
	void floodfill(int &volume,int &area)
	{
		int empty_volume = 0;
		queue<Cell> q;
		Cell start(0, 0, 0);

		size_t nx = x_cors.size();
		size_t ny = y_cors.size();
		size_t nz = z_cors.size();

		//记录某个网格(它一定是空气)是否被访问过,用于防止bfs中重复访问
		vector<vector<vector<bool> > > 
			visited_flag(nx,vector<vector<bool> >(ny,vector<bool>(nz,false)));  

		q.push(start);
		visited_flag[0][0][0] = true;

		while (!q.empty())
		{
			Cell cell = q.front();
			//cout << "cell=";
			//cell.print();
			q.pop();
			int t1 = get_volume(cell);   
			//cout << ",volume=" << t1;
			empty_volume += t1;  //在这里累加空气体积,而不是在下面的for循环内部累加每个邻居
			//那样的话会把第一个节点的体积漏算!

			//对cell的每个邻居进行处理
			for (int dir=0;dir<6;++dir)
			{
				Cell neighbour = cell;
				neighbour.move(dir);
				if (in_side(neighbour))
				{
					//是空气,且没访问过
					if (grid_color[neighbour.xindex][neighbour.yindex][neighbour.zindex]
						==EMPTY
						&& !visited_flag[neighbour.xindex][neighbour.yindex][neighbour.zindex]
						)
					{
						visited_flag[neighbour.xindex][neighbour.yindex][neighbour.zindex] = true;
						
						q.push(neighbour);  //把这个邻居放入队列
					}
					else if (grid_color[neighbour.xindex][neighbour.yindex][neighbour.zindex]
						== SOLID)
					{
						//邻居是雕塑的一部分,则把与空气接触的表面积累加进去
						int t2= get_area(cell, dir);
						/*
						上面的式子不能写成
						int t2= get_area(neighbour, dir);
						因为邻居的表面不一定于空气全接触!!!!!
						*/

						area += t2;
						//cout << ",dir=" << dir << ",area=" << t2;
					}
				}
			}
			//cout << endl;
		}

		int v = get_total_volume();
		volume = v - empty_volume;
	}
};
void main_proc()
{
	int casenum = 0;
	cin >> casenum;
	while(casenum--)
	{
		int N;
		cin >> N;
		vector<Box> boxes(N);
		for (int i=0;i<N;++i)
			boxes[i].read();
		Sculpture s;
		s.init(boxes);
		int volume = 0;
		int area = 0;
		s.floodfill(volume, area);
		cout << area  << " "<< volume << endl;
	}
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值