zoukankan      html  css  js  c++  java
  • 【BZOJ1976】[BeiJing2010组队]能量魔方 Cube 最小割

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

    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。 

    题解:经典的最小割模型,只不过变成了三维的。先黑白染色,然后黑色的点翻转源汇,具体方法:

    1.S ->i 容量为i周围已确定的N个数
    2.i -> T 容量为i周围已确定的P个数
    上面2条边要翻转源汇
    3.i <-> j 容量1

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <queue>
    using namespace std;
    int n,ans,cnt,tot,S,T;
    int dx[]={0,0,0,0,1,-1},dy[]={0,0,1,-1,0,0},dz[]={1,-1,0,0,0,0};
    int map[50][50][50],to[2000000],next[2000000],val[2000000],d[100000],head[100000];
    char str[50];
    queue<int> q;
    int dfs(int x,int mf)
    {
    	if(x==T)	return mf;
    	int i,k,temp=mf;
    	for(i=head[x];i!=-1;i=next[i])	if(d[to[i]]==d[x]+1&&val[i])
    	{
    		k=dfs(to[i],min(temp,val[i]));
    		if(!k)	d[to[i]]=0;
    		val[i]-=k,val[i^1]+=k,temp-=k;
    		if(!temp)	break;
    	}
    	return mf-temp;
    }
    int bfs()
    {
    	while(!q.empty())	q.pop();
    	memset(d,0,sizeof(d));
    	int i,u;
    	q.push(S),d[S]=1;
    	while(!q.empty())
    	{
    		u=q.front(),q.pop();
    		for(i=head[u];i!=-1;i=next[i])
    		{
    			if(!d[to[i]]&&val[i])
    			{
    				d[to[i]]=d[u]+1;
    				if(to[i]==T)	return 1;
    				q.push(to[i]);
    			}
    		}
    	}
    	return 0;
    }
    inline void add(int a,int b,int c)
    {
    	to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
    	to[cnt]=a,val[cnt]=0,next[cnt]=head[b],head[b]=cnt++;
    }
    int main()
    {
    	int i,j,k,l,x,y,z,a,b,c;
    	scanf("%d",&n);
    	S=0,T=tot=1;
    	for(i=1;i<=n;i++)	for(j=1;j<=n;j++)
    	{
    		scanf("%s",str+1);
    		for(k=1;k<=n;k++)
    		{
    			if(str[k]=='P')	map[i][j][k]=1;
    			if(str[k]=='N')	map[i][j][k]=0;
    			if(str[k]=='?')	map[i][j][k]=++tot;
    		}
    	}
    	memset(head,-1,sizeof(head));
    	for(i=1;i<=n;i++)	for(j=1;j<=n;j++)	for(k=1;k<=n;k++)
    	{
    		if(map[i][j][k]>1)
    		{
    			a=b=0,c=map[i][j][k];
    			for(l=0;l<6;l++)
    			{
    				x=i+dx[l],y=j+dy[l],z=k+dz[l];
    				if(x&&y&&z&&x<=n&&y<=n&&z<=n)
    				{
    					if(map[x][y][z]==0)	b++;
    					if(map[x][y][z]==1)	a++;
    					if(map[x][y][z]>1&&((i^j^k)&1))	add(c,map[x][y][z],1),add(map[x][y][z],c,1),ans++;
    				}
    			}
    			if((i^j^k)&1)	swap(a,b);
    			add(S,c,a),add(c,T,b),ans+=a+b;
    		}
    		if(map[i][j][k]==0)
    		{
    			for(l=0;l<6;l++)
    			{
    				x=i+dx[l],y=j+dy[l],z=k+dz[l];
    				if(x&&y&&z&&x<=n&&y<=n&&z<=n&&map[x][y][z]==1)	ans++;
    			}
    		}
    	}
    	while(bfs())	ans-=dfs(S,1<<30);
    	printf("%d",ans);
    	return 0;
    }
  • 相关阅读:
    Html5——视频标签使用
    Android的四大组件
    Android 硬编码
    按键事件处理
    android Keycode 完全对照表
    Activity的生命周期
    音乐播放控制
    Android permission 访问权限大全
    制作留言板相关资料
    adb查询log命令
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7603799.html
Copyright © 2011-2022 走看看