题解:[ZJOI2014]璀灿光华
原题链接
OJ | 题号 |
---|---|
洛谷 | 3342 |
loj | 2203 |
bzoj | 3619 |
题目描述
金先生有一个女朋友没名字。她勤劳勇敢、智慧善良。金先生很喜欢她。为此,金先生用\(a^3\)块\(1 \times 1 \times 1\)的独特的水晶制作了一个边长为\(a\)的水晶立方体,他要将这个水晶立方体送给他见过最单纯善良的她。
由于水晶立方体太太,不好运送,金先生还是将它拆开来送出。他相信拼好这个水晶立方难不倒聪明的她。
没名字收到了礼物后果然不一会儿就根据说明将水晶立方体拼好了。没名字发现,有\(n\)块水晶在漆黑安静的夜晚会随机以等概率向上下左右前后六个方向的一个发出光。被光照到的水晶显得格外好看。没名字给每一块不会发光的水晶定义了一个好看程度。水晶立方体在夜晚中的好看程度就是每块被光照到的水晶的好看程度之和。没名字想知道,水晶立方体在夜晚中的好看程度的最小值和最大值。
输入格式
第一行是 \(a\),表示水晶立方体的边长。
接下来 \(a^3\) 行,每行若干整数。
第一个数\(g_i\)表示第\(i\)块水晶的好看程度。如果 \(g_i=0\),代表这块水晶会发光。接下来 \(3\sim 6\)个整数,代表与这块水晶有共同面的水晶编号。
输出格式
两个整数,代表水晶立方体在夜晚好看程度的最小值与最大值。
样例
样例输入
2
0 7 2 3
0 8 1 4
4 5 4 1
8 6 3 2
16 3 6 7
32 4 5 8
1 1 8 5
2 2 7 6
样例输出
0 12
数据范围与提示
对于所有数据,\(1<a\le 70, gi<1000000, n\le 8\)。
题解
首先,介绍一下c++ stl里的神物——stringstream
。
这东西能像cin
那样读入,但是是从字符串中读入,所以我们就不用打快读了(虽然慢了一点,但开氧气之后海星)。
头文件<sstream>
大概就是这样读:
for(int i = 1; i <= n; ++i)
{
getline(cin, tmp);
stringstream ss(tmp);
ss >> dep[i];
int aa;
while(ss >> aa)
add_edge(i, aa);
}
题目中的照射不仅只照到了与该水晶相邻的水晶,它把整条射线上的水晶全照到了。
所以我们只能考虑建出这个立方体后重新建图。
我的思路是这样的:
先找到一个度为3的点作为一个角的点,用bfs求出其道个点的最短路。记以该点为原点的第\(i\)个点坐标为\((x_{1, i}, y_{1, i}, z_{1, i})\),最短路为\(dist[0][i]\),显然有\(dist[0][i] = x_{1, i} - 1+ y_{1, i} - 1 + z_{1, i} - 1\)。
我们任选一个平面,要求包含刚才那个点。我们找到在该平面上该点对角线上的点,其实就是随便找一个度为3(在角上)且与原点距离\(2(n-1)\)(在该平面上最远)的点。以该点为原点跑一遍最短路,记\(dist[1][i]\)。
由于我们设计最短路时,可以先走到该点正下方,再往上爬,同样,我们可以设计成先走\(y\),再走\(x\),最后走\(z\)。
我们发现\(dist[0][i] +dist[1][i]=2(n-1)\),我们把它减去,就只剩下\(2z\)了,于是\(z\)就求出来了:
/**
ddn是度数
di是原点
poi记录点的x, y, z
这里的n已经是点的个数了
tn才是边长
由于从1开始,所以有些地方微调了一下
*/
for(di[0] = 1; ddn[di[0]] > 3; ++di[0]);
bfs(0);
for(int i = 1; i <= n; ++i)
{
if(ddn[i] == 3 && dist[0][i] == ((tn-1)<<1))
{
di[1] = i;
break;
}
}
bfs(1);
for(int i = 1; i <= n; ++i)
poi[i].z = (dist[0][i] + dist[1][i] - ((tn-2)<<1)) >> 1;
我们可以用同样的方法把\(x\)解出来,最后的\(y\)只要减一下就可以了。因为我的坐标是从1开始的,所以我对坐标做了一些微调,从0开始就没有这个问题。
由于之前bfs的数据我们还能用,所以我们只需再一遍bfs即可
for(int i = 1; i <= n; ++i)
if(poi[i].z == tn && dist[0][i] == dist[1][i] && ddn[i] == 3)
di[2] = i;
bfs(2);
for(int i = 1; i <= n; ++i)
{
poi[i].x = (dist[0][i] + dist[2][i] - ((tn-1)<<1)) >> 1;
poi[i].y = (dist[0][i] - poi[i].x - poi[i].z) + 1;
poi[i].x++;
poi[i].y++;
mmap[poi[i].x][poi[i].y][poi[i].z] = i;
}
于是我们就建好图了。
后面的大力dfs也没什么好说的。
下面把ac代码贴一下,由于stringstream
在没有氧气的情况下极慢,所以必须开氧气。
// luogu-judger-enable-o2
#include <cstring>
#include <iostream>
#include <fstream>
#include <sstream>//stringstream
#include <queue>
using namespace std;
const int maxm = 2058000;//这里的最大边数我是把点数加起来乘了6
const int maxn = 75*75*75;
const int inf = 0x3f3f3f3f;
int n;//总个数
int tn;//边长
struct Edge
{
int to, nxt;
} e[maxm<<1];
int first[maxn];
int cnt;
inline void add_edge(int f, int t)
{
e[++cnt].nxt = first[f];
first[f] = cnt;
e[cnt].to = t;
}
int dirx[] = {1, -1, 0, 0, 0, 0};
int diry[] = {0, 0, 1, -1, 0, 0};
int dirz[] = {0, 0, 0, 0, 1, -1};
int dep[maxn];//"好看程度"
int vis[maxn];
int minn = inf;
int maxx = -inf;
int ll[10];//会发光的水晶的编号
int ddn[maxn];//度数
int zl;//这是个ddn的cnt
int mmap[73][73][73];
struct zb
{
int x, y, z;
} poi[maxn];
#define pan (x > 0 && x <= tn && y > 0 && y <= tn && z > 0 && z <= tn)
#define nxxt x += dirx[i], y += diry[i], z += dirz[i]
inline int getans(int i, zb a)//能加多少"好看程度"
{
int ans = 0;
int x = a.x, y = a.y, z = a.z;
for(; pan; nxxt)
if(!vis[mmap[x][y][z]]++)//由于待会儿回溯时要删除,所以懒到家的我就直接用++,--代替记录了
ans += dep[mmap[x][y][z]];
return ans;
}
inline void delvis(int i, zb a)//回溯时把dfs前加的vis删除
{
int x = a.x, y = a.y, z = a.z;
for(; pan; nxxt)
vis[mmap[x][y][z]]--;
}
inline void dfs(int now, int ans)//大力枚举所有情况
{
if(now > zl)
{
minn = min(minn, ans);
maxx = max(maxx, ans);
return;
}
for(int i = 0; i < 6; ++i)
{
dfs(now+1, ans + getans(i, poi[ll[now]]));
delvis(i, poi[ll[now]]);
}
}
int dist[4][maxn];
int di[4];
bool viss[maxn];
inline void bfs(int id)//求最短路
{
memset(viss, 0, sizeof(viss));
queue<int> q;
int from = di[id];
viss[from] = true;
q.push(from);
while(!q.empty())
{
int now = q.front();
q.pop();
for(int i = first[now]; i; i = e[i].nxt)
{
int to = e[i].to;
if(!viss[to])
{
viss[to] = true;
dist[id][to] = dist[id][now] + 1;
q.push(to);
}
}
}
}
int main()
{
ios:: sync_with_stdio(false);
cin >> n;
tn = n;
string tmp;
getline(cin, tmp);
n *= n * n;
for(int i = 1; i <= n; ++i)
{
getline(cin, tmp);
stringstream ss(tmp);
ss >> dep[i];
int aa;
if(!dep[i])
{
vis[i] = true;
ll[++zl] = i;
}
while(ss >> aa)
{
add_edge(i, aa);
ddn[i]++;
}
}
for(di[0] = 1; ddn[di[0]] > 3; ++di[0]);
bfs(0);
for(int i = 1; i <= n; ++i)
{
if(ddn[i] == 3 && dist[0][i] == ((tn-1)<<1))
{
di[1] = i;
break;
}
}
bfs(1);
for(int i = 1; i <= n; ++i)//得到z
{
poi[i].z = (dist[0][i] + dist[1][i] - ((tn-2)<<1)) >> 1;
if(poi[i].z == tn && dist[0][i] == dist[1][i] && ddn[i] == 3)
di[2] = i;
}
bfs(2);
for(int i = 1; i <= n; ++i)//得到x, y
{
poi[i].x = (dist[0][i] + dist[2][i] - ((tn-1)<<1)) >> 1;
poi[i].y = (dist[0][i] - poi[i].x - poi[i].z) + 1;
poi[i].x++;
poi[i].y++;
mmap[poi[i].x][poi[i].y][poi[i].z] = i;
}
dfs(1, 0);
cout << minn << ' ' << maxx << endl;
fclose(stdin);
fclose(stdout);
return 0;
}
题目描述
金先生有一个女朋友没名字。她勤劳勇敢、智慧善良。金先生很喜欢她。为此,金先生用\(a^3\)块\(1 \times 1 \times 1\)的独特的水晶制作了一个边长为\(a\)的水晶立方体,他要将这个水晶立方体送给他见过最单纯善良的她。
由于水晶立方体太太,不好运送,金先生还是将它拆开来送出。他相信拼好这个水晶立方难不倒聪明的她。
没名字收到了礼物后果然不一会儿就根据说明将水晶立方体拼好了。没名字发现,有 nnn 块水晶在漆黑安静的夜晚会随机以等概率向上下左右前后六个方向的一个发出光。被光照到的水晶显得格外好看。没名字给每一块不会发光的水晶定义了一个好看程度。水晶立方体在夜晚中的好看程度就是每块被光照到的水晶的好看程度之和。没名字想知道,水晶立方体在夜晚中的好看程度的最小值和最大值。
输入格式
第一行是 \(a\),表示水晶立方体的边长。
接下来 \(a^3\) 行,每行若干整数。
第一个数\(g_i\)表示第\(i\)块水晶的好看程度。如果 \(g_i=0\),代表这块水晶会发光。接下来 \(3\sim 6\)个整数,代表与这块水晶有共同面的水晶编号。
输出格式
两个整数,代表水晶立方体在夜晚好看程度的最小值与最大值。
样例
样例输入
2
0 7 2 3
0 8 1 4
4 5 4 1
8 6 3 2
16 3 6 7
32 4 5 8
1 1 8 5
2 2 7 6
样例输出
0 12
数据范围与提示
对于所有数据,\(1<a\le 70, gi<1000000, n\le 8\)。
题解
首先,介绍一下c++ stl里的神物——stringstream
。
这东西能像cin
那样读入,但是是从字符串中读入,所以我们就不用打快读了(虽然慢了一点,但开氧气之后海星)。
头文件<sstream>
大概就是这样读:
for(int i = 1; i <= n; ++i)
{
getline(cin, tmp);
stringstream ss(tmp);
ss >> dep[i];
int aa;
while(ss >> aa)
add_edge(i, aa);
}
题目中的照射不仅只照到了与该水晶相邻的水晶,它把整条射线上的水晶全照到了。
所以我们只能考虑建出这个立方体后重新建图。
我的思路是这样的:
先找到一个度为3的点作为一个角的点,用bfs求出其道个点的最短路。记以该点为原点的第\(i\)个点坐标为\((x_{1, i}, y_{1, i}, z_{1, i})\),最短路为\(dist[0][i]\),显然有\(dist[0][i] = x_{1, i} - 1+ y_{1, i} - 1 + z_{1, i} - 1\)。
我们任选一个平面,要求包含刚才那个点。我们找到在该平面上该点对角线上的点,其实就是随便找一个度为3(在角上)且与原点距离\(2(n-1)\)(在该平面上最远)的点。以该点为原点跑一遍最短路,记\(dist[1][i]\)。
由于我们设计最短路时,可以先走到该点正下方,再往上爬,同样,我们可以设计成先走\(y\),再走\(x\),最后走\(z\)。
我们发现\(dist[0][i] +dist[1][i]=2(n-1)\),我们把它减去,就只剩下\(2z\)了,于是\(z\)就求出来了:
/**
ddn是度数
di是原点
poi记录点的x, y, z
这里的n已经是点的个数了
tn才是边长
由于从1开始,所以有些地方微调了一下
*/
for(di[0] = 1; ddn[di[0]] > 3; ++di[0]);
bfs(0);
for(int i = 1; i <= n; ++i)
{
if(ddn[i] == 3 && dist[0][i] == ((tn-1)<<1))
{
di[1] = i;
break;
}
}
bfs(1);
for(int i = 1; i <= n; ++i)
poi[i].z = (dist[0][i] + dist[1][i] - ((tn-2)<<1)) >> 1;
我们可以用同样的方法把\(x\)解出来,最后的\(y\)只要减一下就可以了。因为我的坐标是从1开始的,所以我对坐标做了一些微调,从0开始就没有这个问题。
由于之前bfs的数据我们还能用,所以我们只需再一遍bfs即可
for(int i = 1; i <= n; ++i)
if(poi[i].z == tn && dist[0][i] == dist[1][i] && ddn[i] == 3)
di[2] = i;
bfs(2);
for(int i = 1; i <= n; ++i)
{
poi[i].x = (dist[0][i] + dist[2][i] - ((tn-1)<<1)) >> 1;
poi[i].y = (dist[0][i] - poi[i].x - poi[i].z) + 1;
poi[i].x++;
poi[i].y++;
mmap[poi[i].x][poi[i].y][poi[i].z] = i;
}
于是我们就建好图了。
后面的大力dfs也没什么好说的。
下面把ac代码贴一下,由于stringstream
在没有氧气的情况下极慢,所以必须开氧气。
// luogu-judger-enable-o2
#include <cstring>
#include <iostream>
#include <fstream>
#include <sstream>//stringstream
#include <queue>
using namespace std;
const int maxm = 2058000;//这里的最大边数我是把点数加起来乘了6
const int maxn = 75*75*75;
const int inf = 0x3f3f3f3f;
int n;//总个数
int tn;//边长
struct Edge
{
int to, nxt;
} e[maxm<<1];
int first[maxn];
int cnt;
inline void add_edge(int f, int t)
{
e[++cnt].nxt = first[f];
first[f] = cnt;
e[cnt].to = t;
}
int dirx[] = {1, -1, 0, 0, 0, 0};
int diry[] = {0, 0, 1, -1, 0, 0};
int dirz[] = {0, 0, 0, 0, 1, -1};
int dep[maxn];//"好看程度"
int vis[maxn];
int minn = inf;
int maxx = -inf;
int ll[10];//会发光的水晶的编号
int ddn[maxn];//度数
int zl;//这是个ddn的cnt
int mmap[73][73][73];
struct zb
{
int x, y, z;
} poi[maxn];
#define pan (x > 0 && x <= tn && y > 0 && y <= tn && z > 0 && z <= tn)
#define nxxt x += dirx[i], y += diry[i], z += dirz[i]
inline int getans(int i, zb a)//能加多少"好看程度"
{
int ans = 0;
int x = a.x, y = a.y, z = a.z;
for(; pan; nxxt)
if(!vis[mmap[x][y][z]]++)//由于待会儿回溯时要删除,所以懒到家的我就直接用++,--代替记录了
ans += dep[mmap[x][y][z]];
return ans;
}
inline void delvis(int i, zb a)//回溯时把dfs前加的vis删除
{
int x = a.x, y = a.y, z = a.z;
for(; pan; nxxt)
vis[mmap[x][y][z]]--;
}
inline void dfs(int now, int ans)//大力枚举所有情况
{
if(now > zl)
{
minn = min(minn, ans);
maxx = max(maxx, ans);
return;
}
for(int i = 0; i < 6; ++i)
{
dfs(now+1, ans + getans(i, poi[ll[now]]));
delvis(i, poi[ll[now]]);
}
}
int dist[4][maxn];
int di[4];
bool viss[maxn];
inline void bfs(int id)//求最短路
{
memset(viss, 0, sizeof(viss));
queue<int> q;
int from = di[id];
viss[from] = true;
q.push(from);
while(!q.empty())
{
int now = q.front();
q.pop();
for(int i = first[now]; i; i = e[i].nxt)
{
int to = e[i].to;
if(!viss[to])
{
viss[to] = true;
dist[id][to] = dist[id][now] + 1;
q.push(to);
}
}
}
}
int main()
{
ios:: sync_with_stdio(false);
cin >> n;
tn = n;
string tmp;
getline(cin, tmp);
n *= n * n;
for(int i = 1; i <= n; ++i)
{
getline(cin, tmp);
stringstream ss(tmp);
ss >> dep[i];
int aa;
if(!dep[i])
{
vis[i] = true;
ll[++zl] = i;
}
while(ss >> aa)
{
add_edge(i, aa);
ddn[i]++;
}
}
for(di[0] = 1; ddn[di[0]] > 3; ++di[0]);
bfs(0);
for(int i = 1; i <= n; ++i)
{
if(ddn[i] == 3 && dist[0][i] == ((tn-1)<<1))
{
di[1] = i;
break;
}
}
bfs(1);
for(int i = 1; i <= n; ++i)//得到z
{
poi[i].z = (dist[0][i] + dist[1][i] - ((tn-2)<<1)) >> 1;
if(poi[i].z == tn && dist[0][i] == dist[1][i] && ddn[i] == 3)
di[2] = i;
}
bfs(2);
for(int i = 1; i <= n; ++i)//得到x, y
{
poi[i].x = (dist[0][i] + dist[2][i] - ((tn-1)<<1)) >> 1;
poi[i].y = (dist[0][i] - poi[i].x - poi[i].z) + 1;
poi[i].x++;
poi[i].y++;
mmap[poi[i].x][poi[i].y][poi[i].z] = i;
}
dfs(1, 0);
cout << minn << ' ' << maxx << endl;
fclose(stdin);
fclose(stdout);
return 0;
}
题解:[ZJOI2014]璀灿光华的更多相关文章
- BZOJ3619 : [Zjoi2014]璀灿光华
终于把省选时的遗憾补上了… 对于构造立方体: 首先BFS构出底层,然后再逐层构造立方体 对于计算: $O(n^6)$爆搜即可. #include<cstdio> #include<c ...
- BZOJ3619 [Zjoi2014]璀灿光华 构造+dfs
题意:有一个\(a^3\)个小正方体组成的大正方体,其中有n个正方体会向上下左右前后六个方向中的一个发出光,正方体是透光的,被照亮的正方体有个美丽值\(g_{i}\),给出正方体的相邻关系,问美丽值之 ...
- 「ZJOI2014」璀灿光华
「ZJOI2014」璀灿光华 实际上,可以不用建水晶立方体... 因为,发光水晶的方向都要枚举一遍. 只需知道发光水晶每个方向有哪些水晶就可以了. 对于一个发光水晶,将它连接的水晶标号. 从该水晶bf ...
- [题解] [ZJOI2014] 力
题面 题解 恩, 我们首先有这两个关系 \[ \displaystyle\begin{aligned} F_j &= \sum_{i < j}\frac{q_iq_j}{(i - j)^ ...
- ZJOI2017 Day1
私のZJOI Day1 2017-3-21 07:52:53 有人在暴力膜 苟-- 富贵 无相忘 ZJOI2017交流群 133135071 如果你足够厉害 如果你足够厉害 如果你足够厉害 其实完全可 ...
- bzoj AC倒序
Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...
- #知识#室内设计原理ing
室内设计原理 第一章 室内设计的含义和基本观点 人的一生,绝大部分时间是在室内度过的,因此,人们设计创造的室内环境,必然会直接关系到室内生活.生产活动的质量,关系到人们的安全.健康.效率.舒适等等.室 ...
- 逐浪web无障碍与国际化以及全民族语言支持白皮书
北京时间2019年5月10日,领先的门户网站与WEB内核服务厂商--上海Zoomla!逐浪CMS团队发布其年度重榜产品:逐浪CMS全民族语言与国际版,体验站点:http://demo2.z01.com ...
- bzoj 3528 [ZJOI2014] 星系调查 题解
[原题] 星系调查 [问题描写叙述] 银河历59451年.在银河系有许很多多已被人类殖民的星系.如果想要在行 星系间往来,大家一般使用连接两个行星系的跳跃星门. 一个跳跃星门能够把 物质在它所连接的 ...
随机推荐
- js 三种存储方式的区别
javaScript有三种数据存储方式,分别是: sessionStorage localStorage cookie 相同点:都保存在浏览器端,同源的 不同点: ①传递方式不同 cookie数据始终 ...
- Jenkins配置:添加用户和管理权限
Jenkins配置:添加用户和管理权限 参考文章:http://www.cnblogs.com/zz0412/p/jenkins_jj_14.html 今天给大家说说使用Jenkins专有用户数据库的 ...
- hdu6514 一维化 + 二维前缀和
http://acm.hdu.edu.cn/showproblem.php?pid=6514 题意 给出一个大矩形(\(nm\leq10^7\)),有p个矩形覆盖,然后有q次询问,询问指定矩形内是否覆 ...
- POJ2976Dropping tests(分数规划)
传送门 题目大意:n个二元组a[i],b[i],去掉k个,求sigma a[i]/ sigma b[i]的最大值 代码: #include<iostream> #include<cs ...
- H5视频、音频不能自动播放,Uncaught (in promise) DOMException: play() failed because the user didn't
错误原因:Chrome的autoplay政策在2018年4月做了更改. 解决办法: 第一步,在chrome浏览器中输入:chrome://flags/#autoplay-policy 第二步,在Aut ...
- 24种设计模式-策略模式(Strategy Pattern)
一.优点: 1. 策略模式提供了管理相关的算法族的办法.策略类的等级结构定义了一个算法或行为族.恰当使用继承可以把公共的代码转移到父类里面,从而避免重复的代码. 2. 策略模式提供了可以替换继承关系的 ...
- B1013(通过)
这种方法是采用B1017的那个求素数的算法,并且送一个比较大的数值当作上线(20000),也可以进一步压缩,但是这个数已经够用了,就没有再试了. python方便是方便,但是真的慢 def isPri ...
- SpringBoot整合log4j2导入新的依赖出现jar冲突解决
1.问题复现: 之前在SpringBoot中配置整合了log4j2,今天在pom文件中,导入新的依赖(依赖如下)之后, <dependency> <groupId>com.gi ...
- picture元素的使用
前言 相信前端小伙伴们对img元素已经烂熟于心,但不知是否了解picture元素呢? 简单来说,picture元素通过包含一个或多个<source>元素和一个<img>元素再结 ...
- Mysql相关问题-----1045 Access denied for user 'root'@'localhost' (using password: YES)报错
MySQL 连接错误,使用Navicat连接MySQL出现错误:1045 Access denied for user 'root'@'localhost' (using password: YES) ...