zoukankan      html  css  js  c++  java
  • 【bzoj3894】文理分科 网络流最小割

    原文地址:http://www.cnblogs.com/GXZlegend


    题目描述

    文理分科是一件很纠结的事情!(虽然看到这个题目的人肯定都没有纠结过)
    小P所在的班级要进行文理分科。他的班级可以用一个n*m的矩阵进行描述,每个格子代表一个同学的座位。每位同学必须从文科和理科中选择一科。同学们在选择科目的时候会获得一个满意值。满意值按如下的方式得到:
    1.如果第i行第秒J的同学选择了文科,则他将获得art[i][j]的满意值,如果选择理科,将得到science[i][j]的满意值。
    2.如果第i行第J列的同学选择了文科,并且他相邻(两个格子相邻当且仅当它们拥有一条相同的边)的同学全部选择了文科,则他会更开心,所以会增加same_art[i][j]的满意值。
    3.如果第i行第j列的同学选择了理科,并且他相邻的同学全部选择了理科,则增加same_science[i]j[]的满意值。
    小P想知道,大家应该如何选择,才能使所有人的满意值之和最大。请告诉他这个最大值。

    输入

    第一行为两个正整数:n,m
    接下来n术m个整数,表示art[i][j];
    接下来n术m个整数.表示science[i][j];
    接下来n术m个整数,表示same_art[i][j];

    输出

    输出为一个整数,表示最大的满意值之和

    样例输入

    3 4
    13 2 4 13
    7 13 8 12
    18 17 0 5
    8 13 15 4
    11 3 8 11
    11 18 6 5
    1 2 3 4
    4 2 3 2
    3 1 0 4
    3 2 3 2
    0 2 2 1
    0 2 4 4

    样例输出

    152


    题解

    网络流最小割,和 bzoj3438 差不多。

    具体做法:

    1.S向每个学生连边,容量为理科收益;每个学生向T连边,容量为文科收益。

    2.将每个学生组合拆成两个,S与第一个连边,容量为全理科收益,第一个向组合中学生连边,容量为inf;

      组合中学生向第二个连边,容量为inf,第二个向T连边,容量为全文科收益。

    3.跑最小割,答案为总收益-mincut。

    #include <cstdio>
    #include <cstring>
    #include <queue>
    #define inf 0x3fffffff
    using namespace std;
    queue<int> q;
    int head[40000] , to[300000] , val[300000] , next[300000] , cnt = 1 , s , t , dis[40000];
    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] = 0 , next[cnt] = head[y] , head[y] = cnt;
    }
    bool bfs()
    {
    	int x , i;
    	while(!q.empty()) q.pop();
    	memset(dis , 0 , sizeof(dis));
    	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 , m , i , x , ans = 0;
    	scanf("%d%d" , &n , &m);
    	s = 0 , t = 3 * n * m + 1;
    	for(i = 1 ; i <= n * m ; i ++ ) scanf("%d" , &x) , add(s , i , x) , ans += x;
    	for(i = 1 ; i <= n * m ; i ++ ) scanf("%d" , &x) , add(i , t , x) , ans += x;
    	for(i = 1 ; i <= n * m ; i ++ )
    	{
    		scanf("%d" , &x) , add(s , i + n * m , x) , add(i + n * m , i , inf) , ans += x;
    		if(i % m != 0) add(i + n * m , i + 1 , inf);
    		if(i % m != 1) add(i + n * m , i - 1 , inf);
    		if(i > m) add(i + n * m , i - m , inf);
    		if(i <= (n - 1) * m) add(i + n * m , i + m , inf);
    	}
    	for(i = 1 ; i <= n * m ; i ++ )
    	{
    		scanf("%d" , &x) , add(i + 2 * n * m , t , x) , add(i , i + 2 * n * m , inf) , ans += x;
    		if(i % m != 0) add(i + 1 , i + 2 * n * m , inf);
    		if(i % m != 1) add(i - 1 , i + 2 * n * m , inf);
    		if(i > m) add(i - m , i + 2 * n * m , inf);
    		if(i <= (n - 1) * m) add(i + m , i + 2 * n * m , inf);
    	}
    	while(bfs()) ans -= dinic(s , inf);
    	printf("%d
    " , ans);
    	return 0;
    }

     

  • 相关阅读:
    aspx有"记住我"的登录
    Aspx比较简单的登录
    内容显示分页数字分页 aspx
    Ashx登录
    Aspx 验证码_各种封装
    IsPostBack的使用
    Ashx增删改查_动软
    一般处理程序ashx
    dispatch_after
    pch文件的作用
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/6808323.html
Copyright © 2011-2022 走看看