题目大意:
现在有一个长方体的盒子,盒子的个条边都和空间直角坐标系的轴线平行,现盒子里有n个点(1 ≤ n ≤ 6),每个点位于盒子内的不同位置,现在可以在各个点上摆放气球,然后让气球的中心位于点上,然后将气球从体积为0开始吹大,直到气球刚好碰到墙壁或者其它气球为止,点按照一定顺序摆放气球(如果对点标号的话,比如点1 2 3 4 5,现在可以按照2 3 1 4 5也可以按照5 1 3 2 4的顺序摆放并吹大气球),当前的气球吹大后可以包含那些没放气球的点,因此可以有部分点没有摆放气球(位置被其它气球占去)。
现有多个测例(测例数无上限),每个测例中先给出n,然后给出盒子两个对顶角的坐标,之后再给出n个点的坐标,要求一个最佳的放吹气球的顺序,使得气球所占的总体积最大,要求输出气球以外的体积(盒子内),题中所有输入都是整数,范围为[-1000, 1000],要求输出也四舍五入到整数位。
注释代码:
/*
* Problem ID : FZU 1515 Balloons in a Box
* Author : Lirx.t.Una
* Language : Visual C++
* Run Time : 15 ms
* Run Memory : 196 KB
*/
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>
//盒子中任意两点之间距离的最大值
#define INF 2001
//π的AC最小精度(测试过)
#define PI 3.1415926536
//maximum number of points
//盒子中点的最大数量
#define MAXPOINTN 6
//乘方
#define POW(x) ( (x) * (x) )
//计算球体体积
#define VOL(r) ( 4.0 / 3.0 * (r) * (r) * (r) * PI )
#define MAX(x,y) ( (x) > (y) ? (x) : (y) )
#define MIN(x,y) ( (x) < (y) ? (x) : (y) )
using namespace std;
struct Node {//表示点
int x, y, z;//坐标
double r;//气球的半径
Node(void) {}
friend istream &
operator>>( istream &is, Node &node ) {
is >> node.x >> node.y >> node.z;
return is;
}
};
Node O;//盒子的一个源点O
Node o;//盒子源点O的对角点opposite of O
Node p[MAXPOINTN];//point,表示各个点
//index of point,表示点序号的全排列,比如
//数组里存放序列1 2 3 4 5或者2 3 5 4 1
//里面的数字表示点的序号,用来保存全排列
int pi[MAXPOINTN];
double
dtw(int i) {//distance to wall
//点到墙的距离
//i表示点的序号
//distance to O and o,离O一边的墙的距离和离o一边的墙的距离
//最终取里6个面中最小的距离
int dtO, dto;
int tmp;//临时变量
int d;//最终的距离
d = INF;//初始化
//前后墙
dtO = abs(p[i].x - O.x);
dto = abs(p[i].x - o.x);
tmp = MIN( dtO, dto );
d = MIN( d, tmp );
//左右墙
dtO = abs(p[i].y - O.y);
dto = abs(p[i].y - o.y);
tmp = MIN( dtO, dto );
d = MIN( d, tmp );
//上下墙
dtO = abs(p[i].z - O.z);
dto = abs(p[i].z - o.z);
tmp = MIN( dtO, dto );
return (double)MIN( d, tmp );
}
double
dbt( int i, int j ) {//distance between two point
//算点i到点j所代表的气球之间的距离
if ( !p[j].r )//如果j点没有气球(表示j点被包含在了其它气球里了
return (double)INF;//则返回一个无效值
double dx, dy, dz;
double r;//最终确定的i点半径
dx = (double)( p[i].x - p[j].x );
dy = (double)( p[i].y - p[j].y );
dz = (double)( p[i].z - p[j].z );
r = sqrt( POW(dx) + POW(dy) + POW(dz) ) - p[j].r;
//如果r < 0表示当前的i点被包含在了其它气球里了,则返回0将i点的半径设为0
return MAX( r, 0.0 );
}
int
main() {
int n;//点数
//volume,maximum volume,volume of box
//当前全排列中所有气球的体积
//体积最大的一个全排列(气球的体积)
//盒子的体积
double v, mv, bv;
double r;//当前点的半径
double tmp;//临时变量
int i, j;//计数变量
while ( ~scanf("%d", &n) ) {
for ( i = 0; i < n; i++ )
pi[i] = i;//每个测例开始先将全排列设置为最小的那个,即逆序数为0的那个
cin >> O >> o;//输入盒子对顶点的坐标,并计算盒子体积
bv = fabs( (double)( O.x - o.x ) * (double)( O.y - o.y ) * (double)( O.z - o.z ) );
for ( i = 0; i < n; i++ )
cin >> p[i];//接下来输入每个点的坐标
mv = 0.0;//初始化最大全排列体积
do {//一个个枚举全排列(决定了放置气球的顺序)
v = 0.0;//每个全排列开始都将其总体积初始化为0
for ( i = 0; i < n; i++ )
p[i].r = 0.0;//注意!!一开始气球都没有摆放,因此所有点的半径都是0
for ( i = 0; i < n; i++ ) {//计算各个点的半径
r = dtw( pi[i] );//先初始化为离墙的距离
for ( j = 0; j < i; j++ ) {
tmp = dbt( pi[i], pi[j] );
r = MIN( r, tmp );
}//取离墙的距离和之前个点摆放气球之间距离的最小值作为最终的半径
p[ pi[i] ].r = r;//确定半径后
if ( r )//累加气球体积
v += VOL(r);
}
mv = MAX( mv, v );
} while ( next_permutation(pi, pi + n) );//该函数可以取得下一个全排列
//next表示下一个全排列比当前的逆序数多,最后一个全排列逆序数最多(即倒序)
//还有一个函数时prev_permutation,就是求上一个全排列,即逆序数减小
printf("%.0lf\n", bv - mv);//求剩余空间大小并打印
}
return 0;
}
无注释代码:
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>
#define INF 2001
#define PI 3.1415926536
#define MAXPOINTN 6
#define POW(x) ( (x) * (x) )
#define VOL(r) ( 4.0 / 3.0 * (r) * (r) * (r) * PI )
#define MAX(x,y) ( (x) > (y) ? (x) : (y) )
#define MIN(x,y) ( (x) < (y) ? (x) : (y) )
using namespace std;
struct Node {
int x, y, z;
double r;
Node(void) {}
friend istream &
operator>>( istream &is, Node &node ) {
is >> node.x >> node.y >> node.z;
return is;
}
};
Node O;
Node o;
Node p[MAXPOINTN];
int pi[MAXPOINTN];
double
dtw(int i) {
int dtO, dto;
int tmp;
int d;
d = INF;
dtO = abs(p[i].x - O.x);
dto = abs(p[i].x - o.x);
tmp = MIN( dtO, dto );
d = MIN( d, tmp );
dtO = abs(p[i].y - O.y);
dto = abs(p[i].y - o.y);
tmp = MIN( dtO, dto );
d = MIN( d, tmp );
dtO = abs(p[i].z - O.z);
dto = abs(p[i].z - o.z);
tmp = MIN( dtO, dto );
return (double)MIN( d, tmp );
}
double
dbt( int i, int j ) {
if ( !p[j].r )
return (double)INF;
double dx, dy, dz;
double r;
dx = (double)( p[i].x - p[j].x );
dy = (double)( p[i].y - p[j].y );
dz = (double)( p[i].z - p[j].z );
r = sqrt( POW(dx) + POW(dy) + POW(dz) ) - p[j].r;
return MAX( r, 0.0 );
}
int
main() {
int n;
double v, mv, bv;
double r;
double tmp;
int i, j;
while ( ~scanf("%d", &n) ) {
for ( i = 0; i < n; i++ )
pi[i] = i;
cin >> O >> o;
bv = fabs( (double)( O.x - o.x ) * (double)( O.y - o.y ) * (double)( O.z - o.z ) );
for ( i = 0; i < n; i++ )
cin >> p[i];
mv = 0.0;
do {
v = 0.0;
for ( i = 0; i < n; i++ )
p[i].r = 0.0;
for ( i = 0; i < n; i++ ) {
r = dtw( pi[i] );
for ( j = 0; j < i; j++ ) {
tmp = dbt( pi[i], pi[j] );
r = MIN( r, tmp );
}
p[ pi[i] ].r = r;
if ( r )
v += VOL(r);
}
mv = MAX( mv, v );
} while ( next_permutation(pi, pi + n) );
printf("%.0lf\n", bv - mv);
}
return 0;
}
单词解释:
spherical:adj, 球形的,球面的
balloon:n, 气球
center:vt, 定中心,使聚集在一点
inflate:vt, 充气,膨胀
enclose:vt, 围绕,装入
axes:n, 坐标轴,轴线