【BZOJ 1976】 [BeiJing2010组队]能量魔方 Cube

1976: [BeiJing2010组队]能量魔方 Cube

Time Limit: 10 Sec   Memory Limit: 64 MB
Submit: 854   Solved: 290
[ Submit][ Status][ Discuss]

Description

小C 有一个能量魔方,这个魔方可神奇了,只要按照特定方式,放入不同的 能量水晶,就可以产生巨大的能量。 能量魔方是一个 N*N*N 的立方体,一共用 N3 个空格可以填充能量水晶。 能量水晶有两种: ·一种是正能量水晶(Positive) ·一种是负能量水晶(Negative) 当这个魔方被填满后,就会依据填充的能量水晶间的关系产生巨大能量。对 于相邻两(相邻就是拥有同一个面)的两个格子,如果这两个格子填充的是一正一 负两种水晶,就会产生一单位的能量。而整个魔方的总能量,就是这些产生的能 量的总和。 现在,小 C 已经在魔方中填充了一些水晶,还有一些位置空着。他想知道, 如果剩下的空格可以随意填充,那么在最优情况下,这个魔方可以产生多少能量。

Input

第一行包含一个数N,表示魔方的大小。 接下来 N2 行,每行N个字符,每个字符有三种可能: P:表示此方格已经填充了正能量水晶; N:表示此方格已经填充了负能量水晶; ?:表示此方格待填充。 上述 N*N 行,第(i-1)*N+1~i*N 行描述了立方体第 i 层从前到后,从左到右的 状态。且每 N 行间,都有一空行分隔。

Output

仅包含一行一个数,表示魔方最多能产生的能量

Sample Input

2
P?
??

??
N?

Sample Output

9

HINT

如下状态时,可产生最多的能量。 
PN 
NP 

NP 
NN 

【数据规模】 
10% 的数据N≤3; 
30% 的数据N≤4; 
80% 的数据N≤10; 
100% 的数据N≤40。 


最小割。


【BZOJ 2132】 一样的思路。


只是这道题多了一些已知的格子,那么他对应的点与s/t连inf的边,表示他必须属于s集/t集

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <queue>
#define M 40*40*40+5
#define inf 0x3f3f3f3f
using namespace std;
int fx[10][4],c[45][45][45],num[45][45][45],n,tot=1,s,t,h[M],d[M],v[M],cur[M],ans;
char S[45][45][45];
struct edge
{
	int from,to,cap,flow,ne;
}E[200005];
void Addedge(int from,int to,int cap)
{
	E[++tot]=(edge){from,to,cap,0,h[from]};
	h[from]=tot;
	E[++tot]=(edge){to,from,0,0,h[to]};
	h[to]=tot;
}
bool bfs()
{
	for (int i=s;i<=t;i++)
		v[i]=0;
	v[s]=1;
	d[s]=0;
	queue<int> q;
	q.push(s);
	while (!q.empty())
	{
		int x=q.front();
		q.pop();
		for (int i=h[x];i;i=E[i].ne)
		{
			edge e=E[i];
			if (!v[e.to]&&e.cap>e.flow)
			{
				v[e.to]=1;
				d[e.to]=d[x]+1;
				q.push(e.to);
			}
		}
	}
	return v[t];
}
int dfs(int x,int a)
{
	if (x==t||!a)
		return a;
	int flow=0;
	for (int &i=cur[x];i;i=E[i].ne)
	{
		edge &e=E[i];
		if (d[e.to]!=d[x]+1) continue;
		int f=dfs(e.to,min(a,e.cap-e.flow));
		if (f)
		{
			flow+=f;
			a-=f;
			e.flow+=f;
			E[i^1].flow-=f;
			if (!a) break;
		}
	}
	return flow;
}
int dinic()
{
	int flow=0;
	while (bfs())
	{
		for (int i=s;i<=t;i++)
			cur[i]=h[i];
		flow+=dfs(s,inf);
	}
	return flow;
}
void Paint()
{
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
			c[0][i][j]=(i+j)&1;
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
			for (int k=1;k<=n;k++)
				c[i][j][k]=1^c[i-1][j][k],
				num[i][j][k]=n*n*(i-1)+n*(j-1)+k;
}
void Build()
{
	fx[1][1]=fx[3][2]=fx[5][3]=1;
	fx[2][1]=fx[4][2]=fx[6][3]=-1;
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
			for (int k=1;k<=n;k++)
			{
				int x=num[i][j][k];
				if (c[i][j][k])
				{
					if (S[i][j][k]=='P') Addedge(s,x,inf);
					if (S[i][j][k]=='N') Addedge(x,t,inf);
				}
				else
				{
                    if (S[i][j][k]=='N') Addedge(s,x,inf);
					if (S[i][j][k]=='P') Addedge(x,t,inf);
				}
				for (int p=1;p<=6;p++)
				{
					int ni=i+fx[p][1],nj=j+fx[p][2],nk=k+fx[p][3];
					if (ni<1||nj<1||nk<1||ni>n||nj>n||nk>n) continue;
					Addedge(x,num[ni][nj][nk],1);
					ans++;
				}
			}
	ans>>=1;
}
int main()
{
	scanf("%d",&n);
	s=0,t=n*n*n+1;
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
			scanf("%s",1+S[i][j]);
	Paint();
	Build();
	printf("%d\n",ans-dinic());
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值