• 【bzoj1976】[BeiJing2010组队]能量魔方 Cube 网络流最小割


    题目描述

    一个n*n*n的立方体,每个位置为0或1。有些位置已经确定,还有一些需要待填入。问最后可以得到的 相邻且填入的数不同的点对 的数目最大。

    输入

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

    输出

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

    样例输入

    2
    P?
    ??

    ??
    N?

    样例输出

    9


    题解

    网络流最小割

    经典的最小割建模了,这里讲一下做法吧:

    由于要求数目最大,因此将其转化为 总可能数目-不满足条件的数目 ,于是就是要最小化不满足条件的数目。

    考虑什么样的不满足条件:选择相同。因此当选择相同时应产生1的代价,最小化这个代价,即最小割问题。

    对立方体黑白染色,然后:

    对于白点:如果确定了为0则S向其连容量为inf的边,如果确定了为1则其向T连容量为inf的边;

    对于黑点:反转源汇,即0连T,1连S。

    对于相邻的两个点,在它们之间连容量为1的双向边。

    跑最小割即为最小的不满足条件的数目。

    讲道理这种经典建模现在看来还是蛮简单的。

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #define N 125010
    #define M 1500010
    #define inf 1 << 30
    #define pos(i , j , k) (i - 1) * n * n + (j - 1) * n + k
    using namespace std;
    queue<int> q;
    int head[N] , to[M] , val[M] , next[M] , cnt = 1 , s , t , dis[N];
    char str[60];
    inline void add(int x , int y , int z)
    {
    	to[++cnt] = y , val[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
    	to[++cnt] = x , val[cnt] = z , next[cnt] = head[y] , head[y] = cnt;
    }
    bool bfs()
    {
    	int x , i;
    	memset(dis , 0 , sizeof(dis));
    	while(!q.empty()) q.pop();
    	dis[s] = 1 , q.push(s);
    	while(!q.empty())
    	{
    		x = q.front() , q.pop();
    		for(i = head[x] ; i ; i = next[i])
    		{
    			if(val[i] && !dis[to[i]])
    			{
    				dis[to[i]] = dis[x] + 1;
    				if(to[i] == t) return 1;
    				q.push(to[i]);
    			}
    		}
    	}
    	return 0;
    }
    int dinic(int x , int low)
    {
    	if(x == t) return low;
    	int temp = low , i , k;
    	for(i = head[x] ; i ; i = next[i])
    	{
    		if(val[i] && dis[to[i]] == dis[x] + 1)
    		{
    			k = dinic(to[i] , min(temp , val[i]));
    			if(!k) dis[to[i]] = 0;
    			val[i] -= k , val[i ^ 1] += k;
    			if(!(temp -= k)) break;
    		}
    	}
    	return low - temp;
    }
    int main()
    {
    	int n , i , j , k , ans = 0;
    	scanf("%d" , &n) , s = 0 , t = n * n * n + 1;
    	for(i = 1 ; i <= n ; i ++ )
    	{
    		for(j = 1 ; j <= n ; j ++ )
    		{
    			scanf("%s" , str + 1);
    			for(k = 1 ; k <= n ; k ++ )
    			{
    				if((i + j + k) & 1)
    				{
    					if(str[k] == 'P') add(s , pos(i , j , k) , inf);
    					if(str[k] == 'N') add(pos(i , j , k) , t , inf);
    				}
    				else
    				{
    					if(str[k] == 'P') add(pos(i , j , k) , t , inf);
    					if(str[k] == 'N') add(s , pos(i , j , k) , inf);
    					if(i > 1) add(pos(i , j , k) , pos(i - 1 , j , k) , 1) , ans ++ ;
    					if(i < n) add(pos(i , j , k) , pos(i + 1 , j , k) , 1) , ans ++ ;
    					if(j > 1) add(pos(i , j , k) , pos(i , j - 1 , k) , 1) , ans ++ ;
    					if(j < n) add(pos(i , j , k) , pos(i , j + 1 , k) , 1) , ans ++ ;
    					if(k > 1) add(pos(i , j , k) , pos(i , j , k - 1) , 1) , ans ++ ;
    					if(k < n) add(pos(i , j , k) , pos(i , j , k + 1) , 1) , ans ++ ;
    				}
    			}
    		}
    	}
    	while(bfs()) ans -= dinic(s , inf);
    	printf("%d
    " , ans);
    	return 0;
    }
    

     

  • 相关阅读:
    DevOps
    DevOps
    Jenkins
    43、android:screenOrientation
    42、使用存放在存assets文件夹下的SQLite数据库
    41、Android中当数据库需要更新时我们该怎么办?
    40、DrawerLayout使用详情
    用Java来写常见的排序算法
    Android高手速成
    16进制 ,Color,Colour转换
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7598657.html
Copyright © 2020-2023  润新知