Luogu P4313 【文理分科】

Description

传送门


Solution

每个点只有两种选择,不是选文科就是选理科,从\(S\)向每个同学连容量为选文科的满意值的有向边,从每个同学向\(T\)连容量为选理科的满意值的有向边,那么总的满意值减去最小割就是答案。

现在考虑如何在所有相邻的同学选同一个课时增加满意值,反过来看也就是说如果相邻的有不选某一门课的就不会增加满意值。

那么我们对于每一个同学新建一个节点,向她还有她周围的所有同学连容量为\(INF\)的边,再从原点向新建的节点连一条容量为这些同学全选文科时增加的满意值。因为是一个二分图,所以如果选了理科那么所有跟\(S\)相连的边都必须被割掉,也就是说如果这些相邻的同学中有一人选择了理科,那么从\(S\)到这些同学的路径都得割掉。而\(INF\)边是割不了的,所以只能割掉新建的节点和\(S\)之间的边,也就满足了要求,对于其他情况同理。


Code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue> 
using namespace std;

const int N = 1000050;
const int M = 2000050;
const int INF = 999999999;

int head[N], num = 1, n, m, s, t, sum, d[N], x, cnt;

struct Node
{
	int next, to, flow;
} edge[M * 2];

void Addedge(int u, int v, int w)
{
	edge[++num] = (Node){head[u], v, w};
	head[u] = num;
	return;
}

void Add(int u, int v, int w)
{
	Addedge(u, v, w);
	Addedge(v, u, 0);
	return;
}

int Id(int a, int b)
{
	return (a - 1) * m + b;
} 

template <class T>
void Read(T &x)
{
	x = 0; int p = 0; char st = getchar();
	while (st < '0' || st > '9') p = (st == '-'), st = getchar();
	while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) +st - '0', st = getchar();	
	x = p ? -x : x;
	return;
} 

template <class T>
void Put(T x)
{
	if (x < 0) putchar('-'), x = -x;
	if (x > 9) Put(x / 10);
	putchar(x % 10 + '0');
	return; 
}

bool Bfs()
{
    queue<int> q;
    for (int i = 0; i <= cnt; i++) d[i] = 0;
    d[s] = 1; q.push(s);
    while (!q.empty())
    {
        int u = q.front(); q.pop();
        for (int i = head[u]; i; i = edge[i].next)
            if (!d[edge[i].to] && edge[i].flow)
            {
                d[edge[i].to] = d[u] + 1;
                q.push(edge[i].to);
                if (edge[i].to == t) return 1;
            }
    }
    return 0;
}

int Dinic(int x, int flow)
{
    if (x == t || !flow) return flow;
    int rest = flow;
    for (int i = head[x]; i && rest; i = edge[i].next)
        if (edge[i].flow && d[edge[i].to] == d[x] + 1)
        {
            int v = edge[i].to;
            int tmp = Dinic(v, min(rest, edge[i].flow));
            rest -= tmp;
            edge[i].flow -= tmp;
            edge[i ^ 1].flow += tmp;
            if (!tmp) d[v] = 0;
        }
    return flow - rest;
}

int Maxflow()
{
	int maxflow = 0, tmp;
	while (Bfs())
	{
		tmp = Dinic(s, INF);
		if (tmp) maxflow += tmp;
	}
	return maxflow;
}

int main()
{
	Read(n); Read(m);
	t = n * m + 1; cnt = t;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
		{
			Read(x);
			Add(s, Id(i, j), x);
			sum += x;
		} 
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
		{
			Read(x);
			Add(Id(i, j), t, x);
			sum += x;
		}
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
		{
			Read(x);
			sum += x;
			int tmp = ++cnt;
			Add(s, tmp, x);
			Add(tmp, Id(i, j), INF);
			if (i + 1 <= n) Add(tmp, Id(i + 1, j), INF);
			if (i - 1 >= 1) Add(tmp, Id(i - 1, j), INF);
			if (j + 1 <= m) Add(tmp, Id(i, j + 1), INF);
			if (j - 1 >= 1) Add(tmp, Id(i, j - 1), INF); 
		}
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
		{
			Read(x);
			sum += x;
			int tmp = ++cnt;
			Add(tmp, t, x);
			Add(Id(i, j), tmp, INF);
			if (i + 1 <= n) Add(Id(i + 1, j), tmp, INF);
			if (i - 1 >= 1) Add(Id(i - 1, j), tmp, INF);
			if (j + 1 <= m) Add(Id(i, j + 1), tmp, INF);
			if (j - 1 >= 1) Add(Id(i, j - 1), tmp, INF);
		}
	Put(sum - Maxflow());
	return 0;
}
posted @ 2020-06-12 10:41  Tian-Xing  阅读(138)  评论(0编辑  收藏  举报