zoukankan      html  css  js  c++  java
  • P4313 文理分科 最小割

    $ color{#0066ff}{ 题目描述 }$

    文理分科是一件很纠结的事情!(虽然看到这个题目的人肯定都没有纠结过)

    小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想知道,大家应该如何选择,才能使所有人的满意值之和最大。请告诉他这个最大值。

    (color{#0066ff}{输入格式})

    第一行为两个正整数:n,m

    接下来n行m个整数,表示art[i][j];

    接下来n行m个整数.表示science[i][j];

    接下来n行m个整数,表示same_art[i][j];

    接下来n行m个整数,表示same_science[i][j];

    (color{#0066ff}{输出格式})

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

    (color{#0066ff}{输入样例})

    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 
    

    (color{#0066ff}{输出样例})

    152
    

    (color{#0066ff}{数据范围与提示})

    1表示选择文科,0表示选择理科,方案如下:

    1 0 0 1

    0 1 0 0

    1 0 0 0

    N,M<=100,读入数据均<=500

    (color{#0066ff}{题解})

    根据题目,每个人要么选文,要么选理,当然是最小割了

    然后我们考虑建图

    首先最基本的,显然可以弄出n*m个点,s向这些点连文科的边,这些点向t连理科的边

    割掉哪个哪个不选

    对于每一个额外的值,只要那些人中有一个人没符合要求,就必须剪掉!

    也就是说,如果不割掉,那么就联通了

    可以建立一个虚点,s向虚点连文科额外的分,这个虚点向对应的(1-5)个点连inf的边(割不了)

    这样的话,理科有一个边没割(代表有人选理)就不会获得文科额外价值,理科同理也建立虚点

    最后总共的-最大流(最小割)即可

    #include<bits/stdc++.h>
    #define LL long long
    LL in() {
    	char ch; LL x = 0, f = 1;
    	while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    	return x * f;
    }
    const int inf = 0x7ffffff;
    const int maxn = 1e5 + 10; 
    const int maxm = 120;
    struct node {
    	int to, can;
    	node *nxt, *rev;
    	node(int to = 0, int can = 0, node *nxt = NULL): to(to), can(can), nxt(nxt) { rev = NULL; }
    };
    node *head[maxn], *cur[maxn];
    int dep[maxn];
    int wen[maxm][maxm], li[maxm][maxm], ww[maxm][maxm], ll[maxm][maxm];
    int n, m, s, t, tot;
    bool bfs() {
    	for(int i = s; i <= t; i++) dep[i] = 0, cur[i] = head[i];
    	std::queue<int> q;
    	dep[s] = 1;
    	q.push(s);
    	while(!q.empty()) {
    		int tp = q.front(); q.pop();
    		for(node *i = head[tp]; i; i = i->nxt) 
    			if(!dep[i->to] && i->can) 
    				dep[i->to] = dep[tp] + 1, q.push(i->to);
    	}
    	return dep[t];
    }
    int dfs(int x, int change) {
    	if(x == t || !change) return change;
    	int flow = 0, ls;
    	for(node *&i = cur[x]; i; i = i->nxt) {
    		if(dep[i->to] == dep[x] + 1 && (ls = dfs(i->to, std::min(change, i->can)))) {
    			flow += ls;
    			change -= ls;
    			i->can -= ls;
    			i->rev->can += ls;
    			if(!change) break;
    		}
    	}
    	return flow;
    }
    int id(int x, int y) { return (x - 1) * m + y; }
    void add(int from, int to, int can) {
    	head[from] = new node(to, can, head[from]);
    }
    void link(int from, int to, int can) {
    	add(from, to, can), add(to, from, 0);
    	(head[from]->rev = head[to])->rev = head[from];
    }
    int dinic() {
    	int flow = 0;
    	while(bfs()) flow += dfs(s, inf);
    	return flow;
    }
    		
    int main() {
    	n = in(), m = in();
    	for(int i = 1; i <= n; i++)
    		for(int j = 1; j <= m; j++)
    			wen[i][j] = in();
    	for(int i = 1; i <= n; i++)
    		for(int j = 1; j <= m; j++)
    			li[i][j] = in();
    	for(int i = 1; i <= n; i++)
    		for(int j = 1; j <= m; j++)
    			ww[i][j] = in();
    	for(int i = 1; i <= n; i++)
    		for(int j = 1; j <= m; j++)
    			ll[i][j] = in();
    
    
    	
    	int tot = 0;
    	s = 0, t = 3 * n * m + 1;
    	for(int i = 1; i <= n; i++)
    		for(int j = 1; j <= m; j++) {
    			tot += wen[i][j];
    			link(s, id(i, j), wen[i][j]);
    			tot += li[i][j];
    			link(id(i, j), t, li[i][j]);
    		}
    	int now = n * m;
    	for(int i = 1; i <= n; i++) 
    		for(int j = 1; j <= m; j++) {
    			now++;
    			link(s, now, ww[i][j]), tot += ww[i][j];
    			link(now, id(i, j), inf);
    			if(i != 1) link(now, id(i - 1, j), inf);
    			if(i != n) link(now, id(i + 1, j), inf);
    			if(j != 1) link(now, id(i, j - 1), inf);
    			if(j != m) link(now, id(i, j + 1), inf);
    		}
    	for(int i = 1; i <= n; i++) 
    		for(int j = 1; j <= m; j++) {
    			now++;
    			link(now, t, ll[i][j]), tot += ll[i][j];
    			link(id(i, j), now, inf);
    			if(i != 1) link(id(i - 1, j), now, inf);
    			if(i != n) link(id(i + 1, j), now, inf);
    			if(j != 1) link(id(i, j - 1), now, inf);
    			if(j != m) link(id(i, j + 1), now, inf);
    		}
    	printf("%d
    ", tot - dinic());
    	return 0;
    }
    
  • 相关阅读:
    Mockito测试
    linux笔记:shell编程-正则表达式
    linux笔记:shell基础-环境变量配置文件
    linux笔记:shell基础-bash变量
    linux笔记:shell基础-bash基本功能
    linux笔记:shell基础-概述和脚本执行方式
    linux笔记:文件系统管理-fdisk分区
    linux笔记:文件系统管理-分区、文件系统以及文件系统常用命令
    linux笔记:权限管理-sudo
    linux笔记:用户和用户组管理-用户管理命令
  • 原文地址:https://www.cnblogs.com/olinr/p/10538742.html
Copyright © 2011-2022 走看看