【BZOJ3619】璀灿光华(ZJOI2014)-思维+建图

测试地址:璀灿光华
(BZOJ没有题面,所以贴了洛谷的测试地址)
做法: 本题需要用到思维+建图。
首先吐槽一下,这个题目名字是不是应该叫“璀璨”啊…算了算了,强省大佬自有用意,我这种阅读理解 0 0 0分选手还是不要妄加猜测了…
注意到 n n n很小,可以直接暴力枚举, O ( a ⋅ 6 n ) O(a\cdot 6^n) O(a6n)解决这个问题。这个题最难的地方在于,如何通过这些点的相互连接,得出每个点在立方体中的坐标。
首先,度数为 3 3 3的点肯定是立方体的角,我们随便拿一个角作为坐标系的原点,我们发现原点在这样的立方体图中和某个点的最短路,就等于空间坐标系中原点和这个点的曼哈顿距离,也即 x i + y i + z i x_i+y_i+z_i xi+yi+zi。我们可以试图找到原点在立方体某一面对角线上的另一个端点,令这个点坐标为 ( 0 , a − 1 , a − 1 ) (0,a-1,a-1) (0,a1,a1),找的方法就是找前面那个曼哈顿距离等于 2 ( a − 1 ) 2(a-1) 2(a1),而且度数为 3 3 3的点。那么再以这个点为起点处理出最短路,我们又可以得到每个点的一个曼哈顿距离: x i + ( a − 1 − y i ) + ( a − 1 − z i ) x_i+(a-1-y_i)+(a-1-z_i) xi+(a1yi)+(a1zi)。有了这两个距离,显然这两个距离加起来等于 2 ( a − 1 ) + 2 x i 2(a-1)+2x_i 2(a1)+2xi,这样我们就能算出一个坐标了,同理可算出另一个坐标 y i y_i yi,于是 z i z_i zi只要从第一个曼哈顿距离里减一减就出来了。
算出来了编号到坐标之间的转换,就可以直接暴力枚举得到答案了,我们就解决了这一题。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
const int inf=1000000000;
int a,n,c[350010]={0},dis[350010][2]={0};
int x[350010],y[350010],z[350010];
int val[75][75][75],p[10][2]={0},q[350010],h,t;
int mv[6][3]={{1,0,0},{-1,0,0},{0,1,0},{0,-1,0},{0,0,1},{0,0,-1}};
int mxans=0,mnans=inf,nowans=0;
int vis[75][75][75]={0};
vector<int> e[350010];

char read(int &s)
{
	s=0;
	char c;
	c=getchar();
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') s=s*10+c-'0',c=getchar();
	return c;
} 

void bfs(int s,bool type)
{
	h=t=1;
	q[1]=s;
	for(int i=1;i<=a*a*a;i++)
		dis[i][type]=inf;
	dis[s][type]=0;
	while(h<=t)
	{
		int v=q[h++];
		for(int i=0;i<e[v].size();i++)
		{
			int y=e[v][i];
			if (dis[y][type]==inf)
			{
				dis[y][type]=dis[v][type]+1;
				q[++t]=y;
			}
		}
	}
}

void dfs(int step)
{
	if (step==p[0][0])
	{
		mxans=max(mxans,nowans);
		mnans=min(mnans,nowans);
		return;
	}
	step++;
	for(int i=0;i<6;i++)
	{
		int nowx=x[p[step][0]],nowy=y[p[step][0]],nowz=z[p[step][0]];
		while(nowx>=0&&nowx<a&&nowy>=0&&nowy<a&&nowz>=0&&nowz<a)
		{
			if (!vis[nowx][nowy][nowz]) nowans+=val[nowx][nowy][nowz];
			vis[nowx][nowy][nowz]++;
			nowx+=mv[i][0],nowy+=mv[i][1],nowz+=mv[i][2];
		}
		dfs(step);
		nowx=x[p[step][0]],nowy=y[p[step][0]],nowz=z[p[step][0]];
		while(nowx>=0&&nowx<a&&nowy>=0&&nowy<a&&nowz>=0&&nowz<a)
		{
			if (vis[nowx][nowy][nowz]==1) nowans-=val[nowx][nowy][nowz];
			vis[nowx][nowy][nowz]--;
			nowx+=mv[i][0],nowy+=mv[i][1],nowz+=mv[i][2];
		}
	}
}

int main()
{
	read(a);
	for(int i=1;i<=a*a*a;i++)
	{
		read(c[i]);
		if (c[i]==0) p[++p[0][0]][0]=i;
		int nxt;
		char C;
		C=read(nxt);
		e[i].push_back(nxt);
		while(C!='\n'&&C!=EOF) C=read(nxt),e[i].push_back(nxt);
		if (e[i].size()==3) p[++p[0][1]][1]=i;
	}
	
	bfs(p[1][1],0);
	bool flag=0;
	for(int i=2;i<=8;i++)
		if (dis[p[i][1]][0]==2*(a-1))
		{
			bfs(p[i][1],1);
			for(int j=1;j<=a*a*a;j++)
			{
				if (!flag) x[j]=(dis[j][0]+dis[j][1]-2*(a-1))>>1;
				else y[j]=(dis[j][0]+dis[j][1]-2*(a-1))>>1;
			}
			if (!flag) flag=1;
			else break;
		}
	for(int i=1;i<=a*a*a;i++)
	{
		z[i]=dis[i][0]-x[i]-y[i];
		val[x[i]][y[i]][z[i]]=c[i];
	}
	
	dfs(0);
	printf("%d %d",mnans,mxans);
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值