• 网络流


    最大流

    (Dinic)

    直接上代码CwQwC。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    using namespace std;
    
    const int INF = 999999999;
    const int N = 10050;
    const int M = 200050;
    
    int n, m, s, t, head[N], num = 1, dis[N];
    
    struct Node
    {
        int next, to, dis;
    } edge[M * 2];
    
    void Addedge(int u, int v, int w)
    {
        edge[++num]= (Node){head[u], v, w};
        head[u] = num;
    }
    
    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 = 1; i <= n; i++) dis[i] = 0;
        dis[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 (!dis[edge[i].to] && edge[i].dis)
                {
                    dis[edge[i].to] = dis[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].dis && dis[edge[i].to] == dis[x] + 1)
            {
                int v = edge[i].to;
                int tmp = Dinic(v, min(rest, edge[i].dis));
                rest -= tmp;
                edge[i].dis -= tmp;
                edge[i ^ 1].dis += tmp;
                if (!tmp) dis[v] = 0;
            }
        return flow - rest;
    }
    
    int main()
    {
        Read(n); Read(m); Read(s); Read(t);
        int u, v, w;
        for (int i = 1; i <= m; i++)
        {
            Read(u); Read(v); Read(w);
            Addedge(u, v, w); Addedge(v, u, 0);
        }
        int maxflow = 0, tmp;
        while (Bfs())
        {
            tmp = Dinic(s, INF);
            if (tmp) maxflow += tmp;
        }
        Put(maxflow);
        return 0;
    }
    

    士兵占领

    传送门

    (Dining)

    每个(S)向饮料连一条容量为(1)的有向边。

    每个食物向(T)连一条容量为(1)的有向边。

    这样是为了限制每个饮料和食物都只能用一次。

    把每个奶牛拆成两个点,之间连上一条容量为(1)的有向边。

    限制奶牛只会被满足一次要求。

    然后每个奶牛喜欢的饮料向奶牛连容量为(1)的有向边,每个奶牛向它喜欢的食物连容量为(1)的有向边。

    跑最大流即可。

    最大获利

    传送门

    最小路径覆盖问题

    首先每个点只能经过一次,所以把每个点(i)拆成两个点(i_a)(i_b)之间连一条容量为(1)的有向边来限制。

    (S)向每个(i_a)连一条容量为(1)的有向边。

    每个(i_b)(T)连容量为(1)的有向边。

    这样连限制了每个点只能在一条路径上。

    如果原图中有一条边((u,v)),那么就从(u_b)(v_a)连容量为(1)的有向边。

    原图如果每个点都分配一条路径就是点数个路径。

    如果照这样建图跑出来的最大流就是最多可以合并多少路径,每多合并一条路径,总的路径数量就减一。

    那么需要的路径数就是点数减去最大流。

    最长不下降子序列问题

    首先动态规划求出(f_i),表示以第(i)位为开头的最长上升序列的长度,求出最长上升序列长度(k)

    把序列每位(i)拆成两个点(i_a)(i_b),从(i_a)(i_b)连接一条容量为(1)的有向边。

    如果(f_i = k),从(S)(i_a)连接一条容量为(1)的有向边。

    如果(f_i = 1),从(i_b)(T)连接一条容量为(1)的有向边。

    如果(j>i)(a_i < a_j)(f_j + 1 = f _i),从(i_b)(j_a)连接一条容量为(1)的有向边。

    求出最大流就是第二问的结果,第三问只要把((1_a, 1_b))((n_a, n_b))((S, 1_a))((n_b, T))的容量改为(INF)即可。

    方格取数问题

    如果找出哪些点不取就变成最小割问题就可以跑最大流了。

    如果取了一个点那么它四周的点都不能取,于是我们想到可以在中间连容量为(INF)的有向边边来限制。

    继续观察发现不能同时取的两个点的横纵坐标之和的奇偶性不同。

    那么我们按横纵坐标奇偶性分成两类,建立二分图。

    (S)向每个横纵坐标之和为偶数位置连容量为权值的有向边,每个横纵坐标之和为奇数位置向(T)连容量为权值的有向边。

    然后不能同时取的点连上就行了。

    答案就是所有权值之和减去最大流。

    魔术球问题

    首先每个数拆成两个点(i_a)(i_b),如果(j)能放在(i)的上面就从(i_b)(j_a)连容量为(1)的有向边。

    然后和最小路径覆盖问题一样建图。

    发现最小路径覆盖数就是最少所需要的柱子数量。

    一个一个数加进残量网络直到最少所需柱子数量大于(n),答案就是此时加进去的数的个数减一。

    圆桌问题

    (S)向每个单位连容量为(r_i)的有向边;每个桌子向(T)连容量为(c_i)的有向边。

    每个单位向每个桌子连容量为(1)的有向边。

    最大流如果是所有人的总数则有解。

    (happiness)

    传送门

    文理分科

    传送门

    家园 / 星际转移问题

    传送门

    费用流

    (Dinic + Spfa)

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    
    const int INF = 1e9;
    const int N = 100050;
    
    int n, m, s, t, head[N], q[N], cur[N], vis[N], l, r, ans, maxflow, num = 1, dis[N], nxt[N];
    
    struct Node
    {
    	int to, flow, dis;	
    } edge[N * 2];
    
    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; 
    }
    
    void Addedge(int u, int v, int w, int c)
    {
    	edge[++num] = (Node){v, w, c};
    	nxt[num] = head[u];
    	head[u] = num;
    	return;
    }
    
    int Bfs()
    {
    	for (int i = 1; i <= n; i++) vis[i] = 0, dis[i] = INF;
    	dis[s] = 0; l = 0; r = 1; q[1] = s;
    	while (l < r)
    	{
    		int u = q[++l]; vis[u] = 0;
    		for(int i = head[u]; i != -1; i = nxt[i])
    		{
    			int v = edge[i].to;
    			if (dis[u] + edge[i].dis < dis[v] && edge[i].flow > 0)
    			{
    				dis[v] = dis[u] + edge[i].dis;
    				if (!vis[v])
    				{
    					if (dis[v] < dis[q[l + 1]]) q[l--] = v;
    					else q[++r] = v;
    					vis[v] = 1;
    				}
    			}
    		}
    	}
    	return dis[t] != INF;
    }
    
    int Dinic(int x, int flow)
    {
    	if (x == t || !flow) return flow;
    	int add = 0, f;
    	vis[x] = 1;
    	for (int i = cur[x]; i != -1 && flow > 0; i = nxt[i])
    	{
    		cur[x] = i;
    		int v = edge[i].to;
    		if (!vis[v] && dis[v] == dis[x] + edge[i].dis && edge[i].flow > 0 && (f = Dinic(v, min(edge[i].flow, flow))))
    		{
    			add += f;
    			ans += f * edge[i].dis;
    			edge[i].flow -= f;
    			edge[i ^ 1].flow += f;
    			flow -= f;
    		}
    	}
    	vis[x] = 0;
    	return add;
    }
    
    void Solve()
    {
    	while (Bfs())
    	{
    		for (int i = 1; i <= n; i++) cur[i] = head[i];
    		maxflow += Dinic(s, INF);
    	}
    	return;
    }
    
    int main()
    {
    	Read(n); Read(m); Read(s); Read(t);
    	for (int i = 1; i <= n; i++) head[i] = -1, dis[i] = INF;
    	for (int i = 1; i <= m; i++)
    	{
    		int u, v, w, c;
    		Read(u); Read(v); Read(w); Read(c);
    		Addedge(u, v, w, c); Addedge(v, u, 0, -c);
    	}
    	Solve();
    	Put(maxflow); putchar(' '); Put(ans);
    	return 0;
    }
    

    餐巾计划问题

    将每一天拆成两个点分别表示早上和晚上。

    (S)向每天晚上连一条容量为当天使用餐巾数量,费用为零的有向边,表示获得脏餐巾。

    每天早上向(T)连一条容量为当天要用的餐巾数量,费用为零的有向边,表示足量提供所需的餐巾数量。

    每天晚上向第二天晚上连容量为(INF),费用为零的边有向边,表示第一天的餐巾可以留到第二天使用。

    每天晚上向加上快洗时间的那天的早上连容量为(INF),费用为快洗费用的有向边,表示快洗。

    每天晚上向加上慢洗时间的那天的早上连容量为(INF),费用为慢洗费用的有向边,表示慢洗。

    (S)向每天早上连容量为(INF),费用为买餐巾的费用的有向边,表示购买餐巾。

    最长k可重区间集问题

    两种建模方式

    第一种按左端点排序所有区间,把每个区间拆分看做两个顶点(i_a)(i_b)

    建立虚点(S')

    连接(S)(S’)一条容量为(k),费用为(0)的有向边。

    (S')到每个(i_a)连接一条容量为(1),费用为(0)的有向边。

    从每个(i_b)(T)连接一条容量为(1),费用为(0)的有向边。

    从每个顶点(i_a)(i_b)连接一条容量为(1),费用为区间长度的有向边。

    对于每个区间(i),与它右边的不相交的所有区间(j)各连一条容量为(1),费用为(0)的有向边。

    第二种离散化所有区间的端点,把每个端点看做一个顶点。

    同样建立虚点(S')

    (S)到顶点(1)(最左边顶点)连接一条容量为(k),费用为(0)的有向边。

    从顶点(2n)(最右边顶点)到(T)连接一条容量为(k),费用为(0)的有向边。

    从顶点(i)到顶点(i+1),连接一条容量为无穷大,费用为(0)的有向边。

    对于每个区间([a, b]),从(a)对应的顶点(i)(b)对应的顶点(j)连接一条容量为(1),费用为区间长度的有向边。

    数字梯形问题

    点和边都不能相交的情况只需将每个点都拆成两个点(i_a)(i_b),中间连一条容量为(1),费用为点权的的有向边限制每个点只能经过一次,然后原图如果有一条边((i,j)),则把(i_b)(j_a)连一条容量为(1),费用为零的有向边。

    点可以相交只需把点对应的边的容量改成(INF)即可。

    边可以相交只需把边对应的边的容量改成(INF)即可。

    剪刀石头布

    传送门

    UVA1104 芯片难题 Chips Challenge

    传送门

  • 相关阅读:
    小程序-地图-定位在我此时的位置
    小程序-地图-标注点固定在某一点
    小程序-列表块/类式ul-li格式(1)
    表单form-input标签禁止聚焦输入
    forEach与jdk8中的lambda, Stream
    put get & push pull
    事务背书 ACID, CAP, BASE
    懒汉式单例要加volatile吗
    SpringMVC DeferedResult和servlet3.1 AsyncContext异步请求
    netty在rpc MQ中的应用
  • 原文地址:https://www.cnblogs.com/Tian-Xing-Sakura/p/13097924.html
Copyright © 2020-2023  润新知