学习笔记--网络流24题(下)

题目链接:https://www.luogu.com.cn/problem/P3355

技巧:对于棋盘类的题目,考虑黑白染色构图,需要分析同类型颜色之间是否存在互相影响的关系(是否能建边),如果同种颜色之间不存在任何关系,则满足二分图定义,朝着最小覆盖,最大独立集思考即可。本题是求最大独立集

//#define LOCAL
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define mem(a, b) memset(a,b,sizeof(a))
#define sz(a) (int)a.size()
#define INF 0x3f3f3f3f
#define DNF 0x7f
#define DBG printf("this is a input\n")
#define fi first
#define se second
#define mk(a, b) make_pair(a,b)
#define pb push_back
#define LF putchar('\n')
#define SP putchar(' ')
#define p_queue priority_queue
#define CLOSE ios::sync_with_stdio(0); cin.tie(0)
#define sz(a) (int)a.size()

template<typename T>
void read(T &x) {x = 0;char ch = getchar();ll f = 1;while(!isdigit(ch)){if(ch == '-')f *= -1;ch = getchar();}while(isdigit(ch)){x = x * 10 + ch - 48; ch = getchar();}x *= f;}
template<typename T, typename... Args>
void read(T &first, Args& ... args) {read(first);read(args...);}
template<typename T>
void write(T arg) {T x = arg;if(x < 0) {putchar('-'); x =- x;}if(x > 9) {write(x / 10);}putchar(x % 10 + '0');}
template<typename T, typename ... Ts>
void write(T arg, Ts ... args) {write(arg);if(sizeof...(args) != 0) {putchar(' ');write(args ...);}}
using namespace std;

ll gcd(ll a, ll b) {
    return b == 0 ? a : gcd(b, a % b);
}

ll lcm(ll a, ll b) {
    return a / gcd(a, b) * b;
}

const int N = 205;
int n , m;
int a[N][N];
int moven[8][2] = {{-2,-1},{-2,1},{-1,-2},{-1,2},{1,-2},{1,2},{2,-1},{2,1}};
int st, ed, head[N * N], cnt, cur[N*N], level[N*N];
struct node {
    int t, next , c;
}edge[N*N*N];
void add (int f, int t, int c)
{
    edge[cnt].c = c;
    edge[cnt].t = t;
    edge[cnt].next = head[f];
    head[f] = cnt ++;
}
void addedge (int f, int t ,int c)
{
    add (f, t, c);
    add (t, f, 0);
}
bool DinicBfs(int s, int e) //利用广搜给网络图分层
{
    mem(level,0);
    queue<int>q;
    q.push(s);
    level[s] = 1;
    while(!q.empty())
    {
        int root = q.front();
        q.pop();
        for(int i = head[root] ; i != -1 ; i = edge[i].next)
        {
            int v = edge[i].t, c = edge[i].c;
            if(!level[v] && c > 0)
            {
                level[v] = level[root] + 1;
                q.push(v);
            }
        }
    }
    return level[e] != 0; //如果当前网络能到达汇点说明存在增广路
}
int DinicDfs(int root , int flow) //利用DFS在已经分好层的网络中寻找增广路
{
    if(root == ed)
        return flow;
    int newflow , cost = 0;
    for(int& i = cur[root] ; i != -1 ; i = edge[i].next) //当前弧优化,&i代表i改动cur也会改动
    {
        int v = edge[i].t, c = edge[i].c;
        if(level[v] == level[root] + 1 && c > 0 && (newflow = DinicDfs(v,min(c,flow-cost))))//多路增广flow-cost
        {
            cost += newflow;
            edge[i].c -= newflow;
            edge[i^1].c += newflow;
            if(cost == flow) break;
        }
    }
    return cost; //返回当前分层网络中能够寻找到的最大流
}
int Dinic()
{
    int ans = 0;
    while (DinicBfs(st, ed))
    {
        for (int i = st ; i <= ed + 5 ; i ++) //如果顶点标号为0,则从0开始
            cur[i] = head[i];
        while (int add = DinicDfs(st, INF))
            ans += add;
    }
    return ans;
}
int main ()
{
    read (n, m);
    mem (head, -1);
    ed = n * n + 1;
    for (int i = 1 ; i <= m ; i ++)
    {
        int u , v;
        read (u , v);
        a[u][v] = 1;
    }
    for (int i = 1 ; i <= n ; i ++)
    {
        for (int j = 1 ; j <= n ; j ++)
        {
            if (a[i][j] == 1)
                continue;
            if ((i + j) % 2)
            {
                addedge (st, (i - 1) * n + j , 1);
                for (int k = 0 ; k < 8 ; k ++)
                {
                    int nx = i + moven[k][0];
                    int ny = j + moven[k][1];
                    if (a[nx][ny] == 1)
                        continue;
                    if (nx >= 1 && nx <= n && ny >= 1 && ny <= n)
                        addedge ((i - 1) * n + j , (nx-1) * n + ny , 1);
                }
            }
            else
                addedge ((i - 1) * n + j , ed , 1);
        }
    }
    cout << n * n - Dinic() - m << endl;
}

题目链接:https://www.luogu.com.cn/problem/P3356
技巧:一个很好的费用流建模题,以及输出构造方案
对于建图,常规套路拆点之类的。对于构造方案,在残余网络上寻找反边存在流量的边,走过去就好了,注意maxflow一定要加上fw[ed],除非每条边容量为1

//#define LOCAL
#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define mem(a, b) memset(a,b,sizeof(a))
#define sz(a) (int)a.size()
#define INF 0x3f3f3f3f
#define DNF 0x7f
#define DBG printf("this is a input\n")
#define fi first
#define se second
#define mk(a, b) make_pair(a,b)
#define pb push_back
#define LF putchar('\n')
#define SP putchar(' ')
#define p_queue priority_queue
#define CLOSE ios::sync_with_stdio(0); cin.tie(0)
#define sz(a) (int)a.size()

template<typename T>
void read(T &x) {x = 0;char ch = getchar();ll f = 1;while(!isdigit(ch)){if(ch == '-')f *= -1;ch = getchar();}while(isdigit(ch)){x = x * 10 + ch - 48; ch = getchar();}x *= f;}
template<typename T, typename... Args>
void read(T &first, Args& ... args) {read(first);read(args...);}
template<typename T>
void write(T arg) {T x = arg;if(x < 0) {putchar('-'); x =- x;}if(x > 9) {write(x / 10);}putchar(x % 10 + '0');}
template<typename T, typename ... Ts>
void write(T arg, Ts ... args) {write(arg);if(sizeof...(args) != 0) {putchar(' ');write(args ...);}}
using namespace std;

ll gcd(ll a, ll b) {
    return b == 0 ? a : gcd(b, a % b);
}

ll lcm(ll a, ll b) {
    return a / gcd(a, b) * b;
}

const int N = 2005;
int n , m , k;
int moven[2][2] = {{0,1},{1,0}};
int a[N][N], b[N][N], now;
int head[N*N], cnt, maxflow;
int st , ed;
struct node {
    int f, t, next, w, c;
}edge[(N*N) << 1];
void add (int f, int t, int w , int c)
{
    edge[cnt].f = f;
    edge[cnt].w = w;
    edge[cnt].c = c;
    edge[cnt].t = t;
    edge[cnt].next = head[f];
    head[f] = cnt ++;
}
void addedge (int f, int t, int w, int c)
{
    add (f, t, w, c);
    add (t, f, 0, -c);
}
int fw[N*N], vis[N*N], pre[N*N];
ll dis[N*N];
queue <int> q;
bool spfa()
{
    for (int i = st ; i <= ed + 5 ; i ++)
        dis[i] = -INF, fw[i] = INF;
    mem(vis,0);
    pre[st] = pre[ed] = -1;
    q.push(st);
    vis[st] = 1;
    dis[st] = 0;
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for (int i = head[u] ; i != -1 ; i = edge[i].next)
        {
            int v = edge[i].t;
            int w = edge[i].w, c = edge[i].c;
            if(edge[i].w > 0 && dis[u] + c > dis[v])
            {
                dis[v] = dis[u] + c;
                fw[v] = min(fw[u], w);
                pre[v] = i;
                if(!vis[v])
                {
                    q.push(v);
                    vis[v] = 1;
                }
            }
        }
    }
    return pre[ed] != -1;
}
void FYL()
{
    while(spfa())
    {
        maxflow += fw[ed];
        for (int i = pre[ed] ; i != -1 ; i = pre[edge[i].f])
        {
            edge[i].w -= fw[ed];
            edge[i^1].w += fw[ed];
        }
    }
}
void dfs (int x, int y, int u, int id)
{
    for (int i = head[u] ; i != -1 ; i = edge[i].next)
    {
        int v = edge[i].t;
        if (v == st || v == ed || v == u - n * m)
            continue;
        if (!edge[i^1].w) continue;
        edge[i ^ 1].w --;
        if (v > n * m)
        {
            dfs (x, y, v, id);
            return ;
        }
        int kx , ky , mov;
        if (v == b[x][y] + 1)
            kx = x, ky = y + 1 , mov = 1;
        else
            kx = x + 1 , ky = y , mov = 0;
        write (id , mov) , LF;
        dfs (kx , ky , v + n * m , id);
        return ;
    }
}
int main ()
{
    mem (head, -1);
    read (k , m , n);
    now = 0;
    st = 0 , ed = n * m * 2 + 1;
    for (int i = 1 ; i <= n ;  i ++)
    {
        for (int j = 1 ; j <= m ; j ++)
        {
            read (a[i][j]);
            b[i][j] = ++ now;
            if (a[i][j] == 2)
            {
                addedge (b[i][j], b[i][j] + n * m, 1, 1);
                addedge (b[i][j], b[i][j] + n * m, INF, 0);
            }
            if (a[i][j] == 0)
                addedge (b[i][j] , b[i][j] + n * m , INF , 0);
        }
    }
    if (a[1][1] != 1) addedge (st , 1, k , 0);
    for (int i = 1 ; i <= n ; i ++)
        for (int j = 1 ; j <= m ; j ++)
        {
            if (a[i][j + 1] != 1 && b[i][j + 1])
                addedge (b[i][j] + n * m , b[i][j + 1] , INF, 0);
            if (a[i + 1][j] != 1 && b[i + 1][j])
                addedge (b[i][j] + n * m , b[i + 1][j] , INF , 0);
        }
    if (a[n][m] != 1) addedge (b[n][m] + n * m , ed , k , 0);
    FYL ();
    for (int i = 1 ; i <= maxflow ; i ++)
        dfs (1, 1, 1, i);
    return 0;
}

题目链接:https://www.luogu.com.cn/problem/P3358

技巧:一对多的流问题,又不存在相关性,一般可以考虑这种模式的建图,经典问题。如果图不好建立,多考虑限制关系,有时候跳出一般点的关系,考虑和原点的关系也是好的选择,还有补图关系

//#define LOCAL
#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define mem(a, b) memset(a,b,sizeof(a))
#define sz(a) (int)a.size()
#define INF 0x3f3f3f3f
#define DNF 0x7f
#define DBG printf("this is a input\n")
#define fi first
#define se second
#define mk(a, b) make_pair(a,b)
#define pb push_back
#define LF putchar('\n')
#define SP putchar(' ')
#define p_queue priority_queue
#define CLOSE ios::sync_with_stdio(0); cin.tie(0)
#define sz(a) (int)a.size()

template<typename T>
void read(T &x) {x = 0;char ch = getchar();ll f = 1;while(!isdigit(ch)){if(ch == '-')f *= -1;ch = getchar();}while(isdigit(ch)){x = x * 10 + ch - 48; ch = getchar();}x *= f;}
template<typename T, typename... Args>
void read(T &first, Args& ... args) {read(first);read(args...);}
template<typename T>
void write(T arg) {T x = arg;if(x < 0) {putchar('-'); x =- x;}if(x > 9) {write(x / 10);}putchar(x % 10 + '0');}
template<typename T, typename ... Ts>
void write(T arg, Ts ... args) {write(arg);if(sizeof...(args) != 0) {putchar(' ');write(args ...);}}
using namespace std;

ll gcd(ll a, ll b) {
    return b == 0 ? a : gcd(b, a % b);
}

ll lcm(ll a, ll b) {
    return a / gcd(a, b) * b;
}

const int N = 200010 ;
int head[N], cnt;
int n , k, st , ed;
struct node {
    int f, t, next, w, c;
}edge[N * 2];
void add (int f, int t, int w , int c)
{
    edge[cnt].f = f;
    edge[cnt].w = w;
    edge[cnt].c = c;
    edge[cnt].t = t;
    edge[cnt].next = head[f];
    head[f] = cnt ++;
}
void addedge (int f, int t, int w, int c)
{
    add (f, t, w, c);
    add (t, f, 0, -c);
}
int fw[N], vis[N], pre[N], now;
ll dis[N];
queue <int> q;
bool spfa()
{
    for (int i = st ; i <= ed + 5 ; i ++)
        dis[i] = -INF;
    mem(fw,INF);
    mem(vis,0);
    pre[st] = pre[ed] = -1;
    q.push(st);
    vis[st] = 1;
    dis[st] = 0;
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for (int i = head[u] ; i != -1 ; i = edge[i].next)
        {
            int v = edge[i].t;
            int w = edge[i].w, c = edge[i].c;
            if(edge[i].w > 0 && dis[u] + c > dis[v])
            {
                dis[v] = dis[u] + c;
                fw[v] = min(fw[u], w);
                pre[v] = i;
                if(!vis[v])
                {
                    q.push(v);
                    vis[v] = 1;
                }
            }
        }
    }
    return pre[ed] != -1;
}
ll FYL()
{
    ll sum = 0 ;
    while(spfa())
    {
        sum += 1ll * dis[ed] * fw[ed];
        for (int i = pre[ed] ; i != -1 ; i = pre[edge[i].f])
        {
            edge[i].w -= fw[ed];
            edge[i^1].w += fw[ed];
        }
    }
    return sum;
}
map <int,int> id;
struct seg {
    int l , r, len;
}seg[N];

map <int, int> Id, buc ;
int main ()
{
    mem (head, -1);
    read (n, k);
    for (int i = 1 ; i <= n ; i ++)
    {
        read (seg[i].l , seg[i].r);
        seg[i].len = seg[i].r - seg[i].l;
    }
    for (int i = 1 ; i <= n ; ++ i){
        if (!Id.count(seg[i].l)) buc[seg[i].l] ++ ;
        if (!Id.count(seg[i].r)) buc[seg[i].r] ++ ;
    }
    for (auto t = buc.begin() ; t != buc.end() ; ++ t)
        Id[t->first] = ++ now ;
    st = 0 , ed = now + 1;
    addedge (st , 1 , k , 0);
    addedge (now, ed , INF , 0);
    for (int i = 1 ; i < now ; i ++)
        addedge (i , i + 1 , INF , 0);
    for (int i = 1 ; i <= n ; i ++)
        addedge (Id[seg[i].l] , Id[seg[i].r] , 1 , seg[i].len);
    write (FYL()), LF;


}

题目链接:https://www.luogu.com.cn/problem/P4009

技巧:分层图模型网络流或者最短路

//#define LOCAL
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair <int,int>
#define pll pair <long,long>
#define mem(a, b) memset(a,b,sizeof(a))
#define sz(a) (int)a.size()
#define INF 0x3f3f3f3f
#define DNF 0x7f
#define DBG printf("this is a input\n")
#define fi first
#define se second
#define mk(a, b) make_pair(a,b)
#define pb push_back
#define LF putchar('\n')
#define SP putchar(' ')
#define p_queue priority_queue
#define CLOSE ios::sync_with_stdio(0); cin.tie(0)
#define sz(a) (int)a.size()

template<typename T>
void read(T &x) {x = 0;char ch = getchar();ll f = 1;while(!isdigit(ch)){if(ch == '-')f *= -1;ch = getchar();}while(isdigit(ch)){x = x * 10 + ch - 48; ch = getchar();}x *= f;}
template<typename T, typename... Args>
void read(T &first, Args& ... args) {read(first);read(args...);}
template<typename T>
void write(T arg) {T x = arg;if(x < 0) {putchar('-'); x =- x;}if(x > 9) {write(x / 10);}putchar(x % 10 + '0');}
template<typename T, typename ... Ts>
void write(T arg, Ts ... args) {write(arg);if(sizeof...(args) != 0) {putchar(' ');write(args ...);}}
using namespace std;

ll gcd(ll a, ll b) {
    return b == 0 ? a : gcd(b, a % b);
}

ll lcm(ll a, ll b) {
    return a / gcd(a, b) * b;
}

const int N = 205, M = 1e6 + 5;
int n , k , p , b , c;
int head[M] , cnt;
struct node {
    int t , w, next;
}edge[M << 2];
void add (int x1, int y1, int z1, int x2, int y2, int z2, int w)
{
    int u = (z1 - 1) * n * n + (x1 - 1) * n + y1;
    int v = (z2 - 1) * n * n + (x2 - 1) * n + y2;
    edge[cnt].t = v;
    edge[cnt].w = w;
    edge[cnt].next = head[u];
    head[u] = cnt ++;
}
int dis[M], vis[M];
void spfa(int root)
{
    queue <int> q;
    mem (dis, INF);
    mem(vis,0);
    dis[root] = 0;
    vis[root] = 1;
    q.push(root);
    while(!q.empty())
    {
        int no = q.front();
        q.pop();
        vis[no] = 0;
        for(int i = head[no] ; i != -1; i = edge[i].next)
        {
            int v = edge[i].t, w = edge[i].w;
            if(dis[v] > dis[no] + w)
            {
                dis[v] = dis[no] + w;
                if(!vis[v])
                {
                    vis[v] = 1;
                    q.push(v);
                }
            }
        }
    }
}
int main ()
{
    CLOSE;
    mem (head, -1);
    cin >> n >> k >> p >> b >> c;
    for (int i = 1 ; i <= n ; i ++)
        for (int j = 1 ; j <= n ; j ++)
        {
            int x;
            cin >> x;
            for (int l = 1 ; l <= k ; l ++)
                add (i, j, l, i, j, l + 1, 0);
            if (x)
            {
                for (int l = 2 ; l <= k + 1 ; l ++)
                    add (i, j, l, i , j , 1 , p);
                if (i > 1) add (i , j , 1 , i - 1 , j , 2, b);
                if (j > 1) add (i , j , 1 , i , j - 1 , 2, b);
                if (i < n) add (i , j , 1 , i + 1 , j , 2, 0);
                if (j < n) add (i , j , 1 , i , j + 1 , 2, 0);
            }
            else
            {
                for (int l = 1 ; l <= k ; l ++)
                {
                    if (i > 1) add (i , j , l , i - 1 , j , l + 1, b);
                    if (j > 1) add (i , j , l , i , j - 1 , l + 1, b);
                    if (i < n) add (i , j , l , i + 1 , j , l + 1, 0);
                    if (j < n) add (i , j , l , i , j + 1 , l + 1, 0);
                }
                add (i , j , k + 1 , i , j , 1 , p + c);
            }
        }
    spfa (1);
    int ans = INF;
    for (int i = 1 ; i <= k + 1 ; i ++)
        ans = min (ans, dis[i * n * n]);
    cout << ans << endl;
}

题目链接:https://www.luogu.com.cn/problem/P4011

技巧:数据小的话,可以考虑状态压缩进行枚举降低解题难度,状态压缩也会成为一个突破口,如果或你发现一个状态的确定需要有一个可变化但是可以枚举的状态来确定时,并且数据规模很小,基本就是状态压缩了

//#define LOCAL
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair <int,int>
#define pll pair <long,long>
#define mem(a, b) memset(a,b,sizeof(a))
#define sz(a) (int)a.size()
#define INF 0x3f3f3f3f
#define DNF 0x7f
#define DBG printf("this is a input\n")
#define fi first
#define se second
#define mk(a, b) make_pair(a,b)
#define pb push_back
#define LF putchar('\n')
#define SP putchar(' ')
#define p_queue priority_queue
#define CLOSE ios::sync_with_stdio(0); cin.tie(0)
#define sz(a) (int)a.size()

template<typename T>
void read(T &x) {x = 0;char ch = getchar();ll f = 1;while(!isdigit(ch)){if(ch == '-')f *= -1;ch = getchar();}while(isdigit(ch)){x = x * 10 + ch - 48; ch = getchar();}x *= f;}
template<typename T, typename... Args>
void read(T &first, Args& ... args) {read(first);read(args...);}
template<typename T>
void write(T arg) {T x = arg;if(x < 0) {putchar('-'); x =- x;}if(x > 9) {write(x / 10);}putchar(x % 10 + '0');}
template<typename T, typename ... Ts>
void write(T arg, Ts ... args) {write(arg);if(sizeof...(args) != 0) {putchar(' ');write(args ...);}}
using namespace std;

ll gcd(ll a, ll b) {
    return b == 0 ? a : gcd(b, a % b);
}

ll lcm(ll a, ll b) {
    return a / gcd(a, b) * b;
}

const int N = 25;
int n , m , p , k , s;
int edge[N][N][N][N], cnt[N][N];
int key[N][N][105], vis[N][N][1 << 14];
int moven[4][2] = {{0,-1},{0,1},{1,0},{-1,0}};
struct node {
    int x, y, state, d;
};
queue <node> q;
int getState (int x, int y)
{
    int state = 0;
    for (int i = 1 ; i <= cnt[x][y] ; i ++)
        state |= (1<<(key[x][y][i])-1);
    return state;

}
int bfs (int sx, int sy)
{
    int state = getState (sx, sy);
    q.push ({sx, sy, state , 0});
    vis[sx][sy][state] = 1;
    while (!q.empty())
    {
        node no = q.front();
        q.pop();
        int x = no.x , y = no.y;
        int st = no.state , d = no.d;
        if (x == n && y == m)
            return d;
        for (int i = 0 ; i < 4 ; i ++)
        {
            int nx = x + moven[i][0];
            int ny = y + moven[i][1];
            int opt = edge[x][y][nx][ny];
            if (nx < 1 || nx > n || ny < 1 || ny > m || opt < 0 || (opt && !(st&(1<<(opt-1))))) continue;
            int nst = st | getState(nx, ny);
            if (vis[nx][ny][nst]) continue;
            q.push ({nx, ny, nst, d + 1});
            vis[nx][ny][nst] = 1;
        }
    }
    return -1;
}
int main ()
{
    CLOSE;
    cin >> n >> m >> p;
    cin >> k;
    for (int i = 1 ; i <= k ; i ++)
    {
        int x1, y1, x2, y2, g;
        cin >> x1 >> y1 >> x2 >> y2 >> g;
        if (g) edge[x1][y1][x2][y2]  = edge[x2][y2][x1][y1] = g;
        else edge[x1][y1][x2][y2] = edge[x2][y2][x1][y1] = -1;
    }
    cin >> s;
    for (int i = 1 ; i <= s ; i ++)
    {
        int x , y , q;
        cin >> x >> y >> q;
        key[x][y][++ cnt[x][y]] = q;
    }
    cout << bfs (1, 1) << endl;
}

题目链接:https://www.luogu.com.cn/problem/P4012

技巧:这个题,题意太绕了,代码就不自己写了,建图还是很简单的,也是很明显的费用流模板题,有一个小技巧可以学一下:当一个点可以经过无数次,但是权值只有第一次经过时才提供贡献的话,可以先建立一条容量为1,花费为权值的边,用来提供贡献,然后建立容量为INF,权值为0的边,用来经过

#include<bits/stdc++.h>  
using namespace std;  
  
const int maxm=1050;  
const int maxn=1050;  
const int INF=0x3f3f3f3f;  
int s,t,tot,n,m;  
int a,b;  
  
struct Edge{  
    int to,nxt,cap,flow,cost;  
}edge[105000];  
int Head[maxn],tol;  
int mp[maxn][maxn];  
int pre[maxn],dis[maxn];  
bool vis[maxn];  
  
void addedge2(int u,int v,int cap,int cost){  
    edge[tol].to=v;  
    edge[tol].cap=cap;  
    edge[tol].cost=cost;  
    edge[tol].flow=0;  
    edge[tol].nxt=Head[u];  
    Head[u]=tol++;  
}  
  
void addedge(int u,int v,int cap,int cost){  
    addedge2(u,v,cap,cost);  
    addedge2(v,u,0,-cost);  
}  
  
bool spfa(int s,int t){  
    queue<int> q;  
    for(int i=0;i<tot;i++){  
        dis[i]=INF;vis[i]=false;pre[i]=-1;  
    }  
    dis[s]=0;vis[s]=true;  
    q.push(s);  
    while(!q.empty()){  
        int u=q.front();  
        q.pop();  
        vis[u]=false;  
        for(int i=Head[u];i!=-1;i=edge[i].nxt){  
            int v=edge[i].to;  
            if (edge[i].cap>edge[i].flow&&dis[v]>dis[u]+edge[i].cost){  
                dis[v]=dis[u]+edge[i].cost;  
                pre[v]=i;  
                if (!vis[v]){  
                    vis[v]=true;  
                    q.push(v);  
                }  
            }  
        }  
    }  
    if (pre[t]==-1) return false;  
    return true;  
}  
  
int minCostMaxflow(int s,int t,int &cost){  
    int flow=0;  
    cost=0;  
    while(spfa(s,t)){  
        int Min=INF;  
        for(int i=pre[t];i!=-1;i=pre[edge[i^1].to])  
            if (Min>edge[i].cap-edge[i].flow)  
                Min=edge[i].cap-edge[i].flow;  
        for(int i=pre[t];i!=-1;i=pre[edge[i^1].to]){  
            edge[i].flow+=Min;  
            edge[i^1].flow-=Min;  
            cost+=edge[i].cost*Min;  
        }  
        flow+=Min;  
    }  
    return flow;  
}  
  
int main(){  
    //freopen("input.txt","r",stdin);  
    while(scanf("%d%d%d%d",&a,&b,&n,&m)!=EOF){  
        memset(Head,-1,sizeof(Head));  
        int cost,c=0,x,y;  
        tol=s=0;  
        t=(n+1)*(m+1)+1;  
        tot=t+1;  
        for(int i=0;i<=n;i++)  
            for(int j=0;j<=m;j++)  
                mp[i][j]=++c;  
        for(int i=0;i<=n;i++)  
        for(int j=0;j<m;j++){  
            scanf("%d",&c);  
            addedge(mp[i][j],mp[i][j+1],1,-c);  
            addedge(mp[i][j],mp[i][j+1],INF,0);  
        }  
        for(int j=0;j<=m;j++)  
        for(int i=0;i<n;i++){  
            scanf("%d",&c);  
            addedge(mp[i][j],mp[i+1][j],1,-c);  
            addedge(mp[i][j],mp[i+1][j],INF,0);  
        }  
        for(int i=0;i<a;i++){  
            scanf("%d%d%d",&c,&x,&y);  
            addedge(s,mp[x][y],c,0);  
        }  
        for(int i=0;i<b;i++){  
            scanf("%d%d%d",&c,&x,&y);  
            addedge(mp[x][y],t,c,0);  
        }  
        int flow;  
        flow=minCostMaxflow(s,t,cost);  
        printf("%d\n",-cost);  
    }  
    return 0;  
} 

题目链接:https://www.luogu.com.cn/problem/P4016

技巧:纸牌均摊经典模型

//#define LOCAL
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair <int,int>
#define pll pair <long,long>
#define mem(a, b) memset(a,b,sizeof(a))
#define sz(a) (int)a.size()
#define INF 0x3f3f3f3f
#define DNF 0x7f
#define DBG printf("this is a input\n")
#define fi first
#define se second
#define mk(a, b) make_pair(a,b)
#define pb push_back
#define LF putchar('\n')
#define SP putchar(' ')
#define p_queue priority_queue
#define CLOSE ios::sync_with_stdio(0); cin.tie(0)
#define sz(a) (int)a.size()

template<typename T>
void read(T &x) {x = 0;char ch = getchar();ll f = 1;while(!isdigit(ch)){if(ch == '-')f *= -1;ch = getchar();}while(isdigit(ch)){x = x * 10 + ch - 48; ch = getchar();}x *= f;}
template<typename T, typename... Args>
void read(T &first, Args& ... args) {read(first);read(args...);}
template<typename T>
void write(T arg) {T x = arg;if(x < 0) {putchar('-'); x =- x;}if(x > 9) {write(x / 10);}putchar(x % 10 + '0');}
template<typename T, typename ... Ts>
void write(T arg, Ts ... args) {write(arg);if(sizeof...(args) != 0) {putchar(' ');write(args ...);}}
using namespace std;

ll gcd(ll a, ll b) {
    return b == 0 ? a : gcd(b, a % b);
}

ll lcm(ll a, ll b) {
    return a / gcd(a, b) * b;
}

const int N = 1005;
int n, a[N], sum = 0;
int head[N], cnt, r[N];
int st , ed;
struct node {
    int f, t, next, w, c;
}edge[N << 1];
void add (int f, int t, int w , int c)
{
    edge[cnt].f = f;
    edge[cnt].w = w;
    edge[cnt].c = c;
    edge[cnt].t = t;
    edge[cnt].next = head[f];
    head[f] = cnt ++;
}
void addedge (int f, int t, int w, int c)
{
    add (f, t, w, c);
    add (t, f, 0, -c);
}
int fw[N], vis[N], pre[N];
ll dis[N];
queue <int> q;
bool spfa()
{
    mem(dis,INF);
    mem(fw,INF);
    mem(vis,0);
    pre[st] = pre[ed] = -1;
    q.push(st);
    vis[st] = 1;
    dis[st] = 0;
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for (int i = head[u] ; i != -1 ; i = edge[i].next)
        {
            int v = edge[i].t;
            int w = edge[i].w, c = edge[i].c;
            if(edge[i].w > 0 && dis[u] + c < dis[v])
            {
                dis[v] = dis[u] + c;
                fw[v] = min(fw[u], w);
                pre[v] = i;
                if(!vis[v])
                {
                    q.push(v);
                    vis[v] = 1;
                }
            }
        }
    }
    return pre[ed] != -1;
}
ll FYL()
{
    ll sum = 0 ;
    while(spfa())
    {
        sum += 1ll * dis[ed] * fw[ed];
        for (int i = pre[ed] ; i != -1 ; i = pre[edge[i].f])
        {
            edge[i].w -= fw[ed];
            edge[i^1].w += fw[ed];
        }
    }
    return sum;
}
int main ()
{
    mem (head, -1);
    read (n);
    for (int i = 1 ; i <= n ; i ++)
        read (a[i]), sum += a[i];
    sum /= n;
    for (int i = 1 ; i <= n ; i ++)
        a[i] -= sum;
    st = 0 , ed = n + 1;
    for (int i = 1 ; i <= n ; i ++)
    {
        if (a[i] > 0)
            addedge (st, i, a[i], 0);
        if (a[i] < 0)
            addedge (i , ed , -a[i], 0);
    }
    for (int i = 1 ; i <= n ; i ++)
    {
        if (i != 1)
            addedge (i , i - 1, INF, 1);
        if (i != n)
            addedge (i , i + 1, INF , 1);
    }
    addedge (1, n , INF , 1);
    addedge (n, 1 , INF , 1);
    write (FYL()), LF;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值