zoukankan      html  css  js  c++  java
  • 网络流提高专题(洛谷题单)

    网络流从入门到入土 #2

    这份题单UVA的题目偏多。但是洛谷不知道为啥UVA交不上去。所以一般我都是去VJ上刷题。

    较为简单的最大流模板题:

    The Grand Dinner

    这道题就是上一份题单里的圆桌问题。改改输出就好了。

    #pragma GCC optimize(2)
    #include <bits/stdc++.h>
    using namespace std;
    #define pii pair<long long, int>
    typedef long long ll;
    
    const int maxn = 1e6 + 10;
    const int maxm = 1e5 * 2 + 10;
    const ll inf = 1e18;
    const int mod = 1e9 + 7;
    
    
    struct Edge{
        int to;
        int next;
        ll w;
    } edge[maxn*2];
    
    int head[maxn], node_num;
    int cur[maxn];//
    int n, m, S, T, k;//
    int dis[maxn];//Bf
    int cnt = 0;//
    bool vis[maxn];
    
    inline void init(){
        cnt = 0;
        for (int i = 0; i <= node_num; ++ i) head[i] = -1;
    }
    
    inline void Add_Edge(int u, int v, ll w){
        edge[cnt].next = head[u];
        edge[cnt].to = v;
        edge[cnt].w = w;
        head[u] = cnt++;
    
        edge[cnt].next = head[v];
        edge[cnt].to = u;
        edge[cnt].w = 0;
        head[v] = cnt++;
    }
    
    
    inline bool Bfs(){
        for(int i = 0; i <= node_num; ++i) dis[i] = -1; 
        dis[S] = 0;
        queue<int> q;
        q.push(S);
        while(!q.empty()){
            int u = q.front(); q.pop();
            for(int i = head[u]; i != -1; i = edge[i].next){
                int v = edge[i].to;
                if(dis[v] == -1 && edge[i].w){
                    dis[v] = dis[u] + 1;
                    q.push(v);
                }
            }
        }
        return dis[T] != -1;
    }
    
    inline ll dfs(int u, ll flow){
        if(u == T) return flow;
        ll del = flow;
        for(int i = cur[u]; i != -1; i = edge[i].next){
            cur[u] = edge[i].next;
            int v = edge[i].to;
            if (dis[v] == dis[u] + 1 && edge[i].w > 0){
                ll ans = dfs(v, min(del, edge[i].w));
                edge[i].w -= ans;
                edge[i ^ 1].w += ans;
                del -= ans;
                if(del == 0) break;
            }
        }
        return flow - del;
    }
    
    inline ll Dinic() {
        ll ans = 0;
        while(Bfs()) {
            for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
            ans += dfs(S, inf);
        }
        return ans;
    }
    
    int main() {
        while (~scanf("%d %d", &m, &n))
        {
            if (!m && !n) break;
            S = 0, T = n+m+1;
            node_num = T;
            init();
            int rec_n[n+1], rec_m[m+1];
            int sum = 0;
            for (int i = 1; i <= m; ++ i) {
                scanf("%d", &rec_m[i]);
                sum += rec_m[i];
                Add_Edge(S, i, rec_m[i]);
                for (int j = 1; j <= n; ++ j)
                    Add_Edge(i, j+m, 1);
            }
            for (int i = 1; i <= n; ++ i) {
                scanf("%d", &rec_n[i]);
                Add_Edge(i+m, T, rec_n[i]);
            }
            int flag = 0;
            if (Dinic() == sum) flag = 1;
            cout << flag << endl;
            if (flag) {
                vector<int> rec[m+1];
                for (int u = 1; u <= m; ++ u) {
                    for (int j = head[u]; ~j; j = edge[j].next) {
                        if (edge[j].w == 0) {
                            rec[u].push_back(edge[j].to-m);
                        }
                    }
                }
                for (int i = 1; i <= m; ++ i) {
                    cout << rec[i][0];
                    for (int j = 1; j < rec[i].size(); ++ j)
                        cout << " " << rec[i][j];
                    cout << endl;
                }
            }
        }
    
        return 0;
    }
    View Code

    Crimewave

    题意:给你n个点,问你是否存在n条连向矩阵外的不相交路径。

    做法:将每个点拆成两个点。流量为1,能到矩阵外的点连T,开始n个点连S;跑最大流判断是否等于n即可。

    #pragma GCC optimize(2)
    #include <bits/stdc++.h>
    using namespace std;
    #define pii pair<long long, int>
    typedef long long ll;
    
    const int maxn = 1e6 + 10;
    const int maxm = 1e5 * 2 + 10;
    const ll inf = 1e18;
    const int mod = 1e9 + 7;
    
    
    struct Edge{
        int to;
        int next;
        ll w;
    } edge[maxn*2];
    
    int head[maxn], node_num;
    int cur[maxn];//当前弧优化数组
    int n, m, S, T, k;//点数,边数,源点,汇点
    int dis[maxn];//Bfs深度
    int cnt = 0;//边数
    bool vis[maxn];
    
    inline void init(){
        cnt = 0;
        for (int i = 0; i <= node_num; ++ i) head[i] = -1;
    }
    
    inline void Add_Edge(int u, int v, ll w){
        edge[cnt].next = head[u];
        edge[cnt].to = v;
        edge[cnt].w = w;
        head[u] = cnt++;
    
        edge[cnt].next = head[v];
        edge[cnt].to = u;
        edge[cnt].w = 0;
        head[v] = cnt++;
    }
    
    
    inline bool Bfs(){
        for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
        dis[S] = 0;
        queue<int> q;
        q.push(S);
        while(!q.empty()){
            int u = q.front(); q.pop();
            for(int i = head[u]; i != -1; i = edge[i].next){
                int v = edge[i].to;
                if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                    dis[v] = dis[u] + 1;
                    q.push(v);
                }
            }
        }
        return dis[T] != -1;
    }
    
    inline ll dfs(int u, ll flow){
        if(u == T) return flow;
        ll del = flow;
        for(int i = cur[u]; i != -1; i = edge[i].next){
            cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
            int v = edge[i].to;
            if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
                ll ans = dfs(v, min(del, edge[i].w));//木桶原理
                edge[i].w -= ans;//正向弧减增广流量
                edge[i ^ 1].w += ans;//反向弧加增广流量
                del -= ans;//总流量减增广流量
                if(del == 0) break;//总流量为0则不继续增广
            }
        }
        return flow - del;//返回本次增广的流量
    }
    
    inline ll Dinic() {
        ll ans = 0;
        while(Bfs()) {
            for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
            ans += dfs(S, inf);
        }
        return ans;
    }
    int dic[4][2] = {1,0, -1,0, 0,-1, 0,1};
    int main() {
        int Ti;
        scanf("%d", &Ti);
        while (Ti--)
        {
            scanf("%d %d %d", &n, &m, &k);
            S = 0, T = 2*n*m+1;
            node_num = T;
            init();
            int sum = 0;
            for (int i = 1, u, v; i <= k; ++ i) {
                scanf("%d %d", &u, &v);
                int id = (u-1)*m+v;
                Add_Edge(S, id, inf);
            }
            for (int i = 1; i <= n; ++ i) {
                for (int j = 1; j <= m; ++ j) {
                    int id = (i-1)*m+j;
                    Add_Edge(id, id+n*m, 1);
                    for (int k = 0; k < 4; ++ k) {
                        int tx = i + dic[k][0], ty = j + dic[k][1];
                        int id1 = (tx-1)*m+ty;
                        if (tx < 1 || ty < 1 || tx > n || ty > m) {
                            Add_Edge(id+n*m, T, inf);
                            continue;
                        }
                        Add_Edge(id+n*m, id1, inf);
                    }
                }
            }
            if (Dinic() == k) cout << "possible" << endl;
            else cout << "not possible" << endl;
        }
    
        return 0;
    }
    View Code

    Internet Bandwidth

    题目大意:单源单汇无向网络求最大流。

    #pragma GCC optimize(2)
    #include <bits/stdc++.h>
    using namespace std;
    #define pii pair<long long, int>
    typedef long long ll;
    
    const int maxn = 1e6 + 10;
    const int maxm = 1e5 * 2 + 10;
    const ll inf = 1e18;
    const int mod = 1e9 + 7;
    
    
    struct Edge{
        int to;
        int next;
        ll w;
    } edge[maxn*2];
    
    int head[maxn], node_num;
    int cur[maxn];//当前弧优化数组
    int n, m, S, T, k;//点数,边数,源点,汇点
    int dis[maxn];//Bfs深度
    int cnt = 0;//边数
    bool vis[maxn];
    
    inline void init(){
        cnt = 0;
        for (int i = 0; i <= node_num; ++ i) head[i] = -1;
    }
    
    inline void Add_Edge(int u, int v, ll w){
        edge[cnt].next = head[u];
        edge[cnt].to = v;
        edge[cnt].w = w;
        head[u] = cnt++;
    
        edge[cnt].next = head[v];
        edge[cnt].to = u;
        edge[cnt].w = 0;
        head[v] = cnt++;
    }
    
    
    inline bool Bfs(){
        for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
        dis[S] = 0;
        queue<int> q;
        q.push(S);
        while(!q.empty()){
            int u = q.front(); q.pop();
            for(int i = head[u]; i != -1; i = edge[i].next){
                int v = edge[i].to;
                if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                    dis[v] = dis[u] + 1;
                    q.push(v);
                }
            }
        }
        return dis[T] != -1;
    }
    
    inline ll dfs(int u, ll flow){
        if(u == T) return flow;
        ll del = flow;
        for(int i = cur[u]; i != -1; i = edge[i].next){
            cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
            int v = edge[i].to;
            if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
                ll ans = dfs(v, min(del, edge[i].w));//木桶原理
                edge[i].w -= ans;//正向弧减增广流量
                edge[i ^ 1].w += ans;//反向弧加增广流量
                del -= ans;//总流量减增广流量
                if(del == 0) break;//总流量为0则不继续增广
            }
        }
        return flow - del;//返回本次增广的流量
    }
    
    inline ll Dinic() {
        ll ans = 0;
        while(Bfs()) {
            for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
            ans += dfs(S, inf);
        }
        return ans;
    }
    int dic[4][2] = {1,0, -1,0, 0,-1, 0,1};
    
    int main() {
        int tot = 0;
        while (~scanf("%d", &n) && n) {
            scanf("%d %d %d", &S, &T, &m);
            //S = 0, T = 2*n*m+1;
            node_num = n;
            init();
            int sum = 0;
            for (int i = 1, u, v, cap; i <= m; ++ i) {
                scanf("%d %d %d", &u, &v, &cap);
                Add_Edge(u, v, cap), Add_Edge(v, u, cap);
            }
            //cout << Dinic() << endl;
            cout << "Network " << ++tot << endl;
            cout << "The bandwidth is " << Dinic() << "." << endl << endl;
        }
    
        return 0;
    }
    View Code

    Air Raid

    有向无环图(DAG)的最小路径覆盖的模板题。

    #pragma GCC optimize(2)
    #include <bits/stdc++.h>
    using namespace std;
    #define pii pair<long long, int>
    typedef long long ll;
    
    const int maxn = 1e6 + 10;
    const int maxm = 1e5 * 2 + 10;
    const ll inf = 1e18;
    const int mod = 1e9 + 7;
    
    
    struct Edge{
        int to;
        int next;
        ll w;
    } edge[maxn*2];
    
    int head[maxn], node_num;
    int cur[maxn];//当前弧优化数组
    int n, m, S, T, k;//点数,边数,源点,汇点
    int dis[maxn];//Bfs深度
    int cnt = 0;//边数
    bool vis[maxn];
    
    inline void init(){
        cnt = 0;
        for (int i = 0; i <= node_num; ++ i) head[i] = -1;
    }
    
    inline void Add_Edge(int u, int v, ll w){
        edge[cnt].next = head[u];
        edge[cnt].to = v;
        edge[cnt].w = w;
        head[u] = cnt++;
    
        edge[cnt].next = head[v];
        edge[cnt].to = u;
        edge[cnt].w = 0;
        head[v] = cnt++;
    }
    
    
    inline bool Bfs(){
        for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
        dis[S] = 0;
        queue<int> q;
        q.push(S);
        while(!q.empty()){
            int u = q.front(); q.pop();
            for(int i = head[u]; i != -1; i = edge[i].next){
                int v = edge[i].to;
                if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                    dis[v] = dis[u] + 1;
                    q.push(v);
                }
            }
        }
        return dis[T] != -1;
    }
    
    inline ll dfs(int u, ll flow) {
        if(u == T) return flow;
        ll del = flow;
        for(int i = cur[u]; i != -1; i = edge[i].next){
            cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
            int v = edge[i].to;
            if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
                ll ans = dfs(v, min(del, edge[i].w));//木桶原理
                edge[i].w -= ans;//正向弧减增广流量
                edge[i ^ 1].w += ans;//反向弧加增广流量
                del -= ans;//总流量减增广流量
                if(del == 0) break;//总流量为0则不继续增广
            }
        }
        return flow - del;//返回本次增广的流量
    }
    
    inline ll Dinic() {
        ll ans = 0;
        while(Bfs()) {
            for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
            ans += dfs(S, inf);
        }
        return ans;
    }
    int dic[4][2] = {1,0, -1,0, 0,-1, 0,1};
    /*
    2
    4
    3
    3 4
    1 3
    2 3
    3
    3
    1 3
    1 2
    2 3
    */
    int main() {
        int Ti;
        scanf("%d", &Ti);
        while (Ti--) {
            scanf("%d %d", &n, &m);
            S = 0, T = 2*n+1;
            node_num = T;
            init();
            int sum = 0;
            for (int i = 1; i <= n; ++ i) Add_Edge(S, i, 1), Add_Edge(i+n, T, 1);
            for (int i = 1, u, v; i <= m; ++ i) {
                scanf("%d %d", &u, &v);
                Add_Edge(u, v+n, 1);
            }
            cout << n - Dinic() << endl;
    
        }
    
        return 0;
    }
    View Code

     The K-League

    题意:网络流公平分配模型。告诉你有几支队伍,告诉你已知胜场,以及剩下需要进行的场数。问你哪些队伍可能获得胜利。

    附上一个写的很好的博客:网络流和棒球赛淘汰问题 公平分配模板 足球联赛

    (建模图)

    AC代码:

    #pragma GCC optimize(2)
    #include <bits/stdc++.h>
    using namespace std;
    #define pii pair<long long, int>
    typedef long long ll;
    
    const int maxn = 1000 + 10;
    const int maxm = 1e6 * 2 + 10;
    const ll inf = 1e14;
    const int mod = 1e9 + 7;
    
    struct Edge{
        int to;
        int next;
        ll w;
    } edge[maxm];
    
    int head[maxn], node_num;
    int cur[maxn];//当前弧优化数组
    int n, m, S, T, k;//点数,边数,源点,汇点
    int dis[maxn];//Bfs深度
    int cnt = 0;//边数
    bool vis[maxn];
    int w[maxn], d[maxn];
    int a[maxn][maxn];
    inline void init(){
        cnt = 0;
        for (int i = 0; i <= node_num; ++ i) head[i] = -1;
    }
    
    inline void Add_Edge(int u, int v, ll w){
        edge[cnt].next = head[u];
        edge[cnt].to = v;
        edge[cnt].w = w;
        head[u] = cnt++;
    
        edge[cnt].next = head[v];
        edge[cnt].to = u;
        edge[cnt].w = 0;
        head[v] = cnt++;
    }
    
    
    inline bool Bfs(){
        for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
        dis[S] = 0;
        queue<int> q;
        q.push(S);
        while(!q.empty()){
            int u = q.front(); q.pop();
            for(int i = head[u]; i != -1; i = edge[i].next){
                int v = edge[i].to;
                if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                    dis[v] = dis[u] + 1;
                    q.push(v);
                }
            }
        }
        return dis[T] != -1;
    }
    
    inline ll dfs(int u, ll flow){
        if(u == T) return flow;
        ll del = flow;
        for(int i = cur[u]; i != -1; i = edge[i].next){
            cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
            int v = edge[i].to;
            if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
                ll ans = dfs(v, min(del, edge[i].w));//木桶原理
                edge[i].w -= ans;//正向弧减增广流量
                edge[i ^ 1].w += ans;//反向弧加增广流量
                del -= ans;//总流量减增广流量
                if(del == 0) break;//总流量为0则不继续增广
            }
        }
        return flow - del;//返回本次增广的流量
    }
    
    inline ll Dinic() {
        ll ans = 0;
        while(Bfs()) {
            for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
            ans += dfs(S, inf);
        }
        return ans;
    }
    
    bool canWin(int team) {
        //total代表的是team的最大胜场
        int total = w[team];
        for (int i = 0; i < n; ++ i)
            total += a[team][i];
        //如果存在其他队的胜场已经超过team的最大胜场,那么直接返回
        for (int i = 0; i < n; ++ i)
            if (w[i] > total) return 0;
    
        S = 0, T = n*n+n+1;
        node_num = T;
        init();
        int maxflow = 0;
        for (int u = 0; u < n; ++ u) {
            for (int v = u+1; v < n; ++ v) {
                if (a[u][v] > 0) Add_Edge(S, u*n+v+1, a[u][v]);
                maxflow += a[u][v];
                Add_Edge(u*n+v+1, n*n+u+1, inf);
                Add_Edge(u*n+v+1, n*n+v+1, inf);
            }
            if (w[u] < total) Add_Edge(n*n+u+1, T, total-w[u]);
        }
        return Dinic() == maxflow;
    }
    /*
    3
    3
    2 0 1 1 0 2
    0 2 2 2 0 2 2 2 0
    3
    4 0 2 2 0 4
    0 1 1 1 0 1 1 1 0
    4
    0 3 3 1 1 3 3 0
    0 0 0 2 0 0 1 0 0 1 0 0 2 0 0 0
    */
    int main() {
        int T;
        scanf("%d", &T);
        while(T--) {
            scanf("%d", &n);
            for(int i = 0; i < n; i ++)
                scanf("%d%d", &w[i], &d[i]);
            for(int i = 0; i < n; i ++)
                for(int j = 0; j < n; j++)
                    scanf("%d", &a[i][j]);
    
            bool first = true;
            for(int i = 0; i < n; i++)
                if(canWin(i)) {
                    if(first) first = false;
                    else printf(" ");
                    printf("%d", i+1);
                }
            printf("\n");
        }
        return 0;
    }
    View Code

    Jamie's Contact Groups

    这道题就是POJ的2289之前用二分图写过了。二分+网络流check就可以了。

    我记得,这道题目的读取方式非常地让人难受。

        #include<iostream>
        #include<cstdio>
        #include<cstring>
        #include<queue>
        #include<vector>
        #include<sstream>
        using namespace std;
        const int maxn=2010;
        const int maxm=1e6+7;
        const int inf=0x3f3f3f3f;
        #define pb push_back
        struct Node
        {
            int to;
            int capa;
            int next;
        }edge[maxm];
        int n,m;
        int source,sink;
        int cnt;
        int head[maxn];
        bool vis[maxn];
        int dep[maxn];
        char name[100010];
        vector<int> vec[maxn];
        void init()
        {
            memset(head,-1,sizeof(head));
            cnt=0;
            return;
        }
        void add(int u,int v,int capa)
        {
            edge[cnt].to=v;
            edge[cnt].capa=capa;
            edge[cnt].next=head[u];
            head[u]=cnt++;
            edge[cnt].to=u;
            edge[cnt].capa=0;
            edge[cnt].next=head[v];
            head[v]=cnt++;
            return;
        }
        bool bfs()
        {
            queue<int> que;
            que.push(source);
            memset(dep,-1,sizeof(dep));
            dep[source]=0;
            while(!que.empty())
            {
                int node=que.front();
                que.pop();
                for(int i=head[node];~i;i=edge[i].next)
                {
                    int v=edge[i].to;
                    if(edge[i].capa>0&&dep[v]==-1)
                    {
                        dep[v]=dep[node]+1;
                        if(v==sink) return true;
                        que.push(v);
                    }
                }
            }
            return dep[sink]!=-1;
        }
        int dfs(int node,int minn)
        {
            if(node==sink||minn==0)
            {
                return minn;
            }
            int r=0;
            for(int i=head[node];~i;i=edge[i].next)
            {
                int v=edge[i].to;
                if(dep[v]==dep[node]+1&&edge[i].capa>0)
                {
                    int tmp=dfs(v,min(edge[i].capa,minn));
                    if(tmp>0)
                    {
                        edge[i].capa-=tmp;
                        edge[i^1].capa+=tmp;
                        r+=tmp;
                        minn-=tmp;
                        if(!minn) break;
                    }
                }
            }
            if(!r) dep[node]=-1;
            return r;
        }
        int dinic()
        {
            int maxflow=0;
            while(bfs())
            {
                maxflow+=dfs(source,inf);
            }
            return maxflow;
        }
        void build(int mid)
        {
            for(int i=1;i<=n;i++)
            {
                add(source,i,1);
                int len=vec[i].size();
                for(int j=0;j<len;j++)
                {
                    int x=vec[i][j];
                    add(i,n+x+1,1);
                }
            }
            for(int i=1;i<=m;i++)
            {
                add(n+i,sink,mid);
            }
            return;
        }
        int main()
        {
            //freopen("in.txt","r",stdin);
            //freopen("out.txt","w",stdout);
            while(~scanf("%d%d",&n,&m))
            {
                getchar();
                if(!n&&!m) break;
                source=0;
                sink=n+m+10;
                string str;
                int num;
                for(int i=1;i<=n;i++)
                {
                    vec[i].clear();
                    getline(cin,str);
                    stringstream stream(str);
                    stream>>name;
                    while(stream>>num)
                    {
                        vec[i].pb(num);
                    }
                }
                int left=0;
                int right=n;
                while(left<=right)
                {
                    int mid=(left+right)/2;
                    init();
                    build(mid);
                    int ans=dinic();
                    if(ans==n)
                    {
                        right=mid-1;
                    }
                    else
                    {
                        left=mid+1;
                    }
                }
                printf("%d\n",left);
            }
            return 0;
        }
    View Code

    Pool construction

     这是一个最小割的很典型的题目。

     每个洞要么是草地,要么是洞,我们假设草地是S集合,洞是Y集合,然后洞和草之间要建栅栏,也就可以理解为,用最少的花费将S集合和Y集合分开,这就是最小割的模型了。

    初始时,从s点向所有的草地点连一条边,容量为d,割这些边意味着将这个草地变成洞。把每个洞的点向t连一条边,容量为f,割这些边意味着将这个洞变成草。

    相邻的两个格子之间u和v,连两条边,u到v和 v到u,容量为b。割u到v这条边意味着u是草,v是洞,在之间建围栏。割v到u的点也类似。

     题目还有一个要求,矩阵第一行/列和最后一行/列必须是草,所以,s向这些草连边容量为INF,代表这些边不能割

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <iostream>
    #include <queue>
    
    using namespace std;
    const int maxn=3000+10;
    const int maxw=55;
    const int maxm=30000;
    const int INF=2147000000;
    struct Dinic{
        int head[maxn],Next[maxm],to[maxm],cap[maxm],flow[maxm];
        int sz,n,m,s,t;
        bool vis[maxn];
        int cur[maxn],d[maxn];
        void init(int n){
            this->n=n;
            memset(head,-1,sizeof(head));
            sz=-1;
        }
        void add_edge(int a,int b,int c){
            ++sz;
            to[sz]=b;
            cap[sz]=c;flow[sz]=0;
            Next[sz]=head[a];head[a]=sz;
            ++sz;
            to[sz]=a;
            cap[sz]=c;flow[sz]=c;
            Next[sz]=head[b];head[b]=sz;
        }
        bool BFS(){
            memset(vis,0,sizeof(vis));
            queue<int>Q;
            vis[s]=1;
            d[s]=0;
            Q.push(s);
            while(!Q.empty()){
                int u=Q.front();Q.pop();
                for(int i=head[u];i!=-1;i=Next[i]){
                    int v=to[i];
                    if(!vis[v]&&cap[i]>flow[i]){
                        vis[v]=1;
                        d[v]=d[u]+1;
                        Q.push(v);
                    }
                }
            }
            return vis[t];
       }
        int DFS(int x,int a){
            if(x==t||a==0)return a;
            int Flow=0,f;
            for(int& i=cur[x];i!=-1;i=Next[i]){
                int v=to[i];
                if(d[v]==d[x]+1&&(f=DFS(v,min(a,cap[i]-flow[i])))>0){
                    Flow+=f;
                    flow[i]+=f;
                    flow[i^1]-=f;
                    a-=f;
                    if(a==0)break;
                }
            }
            return Flow;
        }
        int Maxflow(int s,int t){
            this->s=s,this->t=t;
            int Flow=0;
            while(BFS()){
                for(int i=0;i<=n;i++)
                 cur[i]=head[i];
                Flow+=DFS(s,INF);
            }
            return Flow;
        }
    }dinic;
    int w,h,d,f,b,T,ans;
    char G[maxw][maxw];
    int main(){
        scanf("%d",&T);
        for(int t=1;t<=T;t++){
            ans=0;
            scanf("%d%d",&w,&h);
            scanf("%d%d%d",&d,&f,&b);
            for(int i=1;i<=h;i++){
                for(int j=1;j<=w;j++){
                    scanf(" %c",&G[i][j]);
                    if(i==1||j==1||i==h||j==w){
                        if(G[i][j]=='.'){
                            G[i][j]='#';
                            ans+=f;
                        }
                    }
                }
            }
            dinic.init(w*h+2);
            for(int i=1;i<=h;i++){
                for(int j=1;j<=w;j++){
                    if(i==1||j==1||i==h||j==w){
                        dinic.add_edge(0,(i-1)*w+j,INF);
                    }else{
                        if(G[i][j]=='#'){
                            dinic.add_edge(0,(i-1)*w+j,d);
                        }
                        if(G[i][j]=='.'){
                            dinic.add_edge((i-1)*w+j,h*w+1,f);
                        }
                    }
                    if(i+1<=h){
                        dinic.add_edge((i-1)*w+j,i*w+j,b);
                        dinic.add_edge(i*w+j,(i-1)*w+j,b);
                    }
                    if(j+1<=w){
                        dinic.add_edge((i-1)*w+j,(i-1)*w+j+1,b);
                        dinic.add_edge((i-1)*w+j+1,(i-1)*w+j,b);
                    }
                }
            }
            ans+=dinic.Maxflow(0,w*h+1);
            printf("%d\n",ans);
        }
    return 0;
    }
    View Code

    Cable TV Network

    这题正好学了一下图论中点连通度和边连通度的概念。

    1.  基本概念

    (1)一个具有 N 个顶点的图,在去掉任意 K-1 个顶点后 (1<=K<=N) 所得的子图仍连通,则称 G 是连通的,

    而去掉 K 个顶点后的图不连通, 那么K 称作图 G 的点连通度

    (2)相应地如果至少去掉 K 条边使这个图不连通,则 K 成为图的边连通度

    2.  求解思路

    •  对于求解边联通度的问题,为每条边赋权值为1,然后求确定一点作为源点,枚举此点外的每个点作为汇点求最大流。
    • 点联通度问题可以转换到边联通度问题上来,具体转换方法如下
      • 若 G 为无向图,假设有n个点:

            (1) 原 G 图中的每个顶点 v 变成两个顶点 v' 和 v+n ,顶点 v 至 v+n 有一条弧(有向边)连接,弧容量为 1;

            (2) 原 G 图中的每条边  e = uv ,连一条 u+n 到 v 的弧,再连一条 v+n 到 u 的弧,容量均为INF

            (3) A” 为源顶点, B' 为汇顶点

             注意:弧是有向边

      • 若 G 为有向图,假设有n个点:

            (1) 原 G 图中的每个顶点 v 变成两个顶点 v' 和 v+n ,顶点 v 至 v+n 有一条弧(有向边)连接,弧容量为 1;

            (2) 原 G 图中的每条弧  e = uv 变成一条有向轨 u'u"v'v" ,其中轨上的弧 u"v' 的容量为 ∞;

            (3) A” 为源顶点, B' 为汇顶点

    • 枚举源点 A" ,枚举汇点B',求 A" 到 B' 的最小最大流 F,注意源点汇点的拆点容量应该为inf(为不可割的点)

    BTW, 这题网上有比较多的做法是只枚举汇点的,但是按理来说应该是源汇都要枚举

    还有些做法虽然枚举了源汇,但是确实以出点作为源点,入点作为汇点。这样是通过网络流特性来使得源汇不可割,但从理解上还是我这种建图方式较好理解

    #pragma GCC optimize(2)
    #include <bits/stdc++.h>
    using namespace std;
    #define pii pair<long long, int>
    typedef long long ll;
    
    const int maxn = 1000 + 10;
    const int maxm = 1e6 * 2 + 10;
    const ll inf = 1e14;
    const int mod = 1e9 + 7;
    
    struct Edge{
        int to;
        int next;
        ll w;
    } edge[maxm];
    
    int head[maxn], node_num;
    int cur[maxn];//当前弧优化数组
    int n, m, S, T, k;//点数,边数,源点,汇点
    int dis[maxn];//Bfs深度
    int cnt = 0;//边数
    bool vis[maxn];
    int w[maxn], d[maxn];
    int a[maxn][maxn];
    inline void init(){
        cnt = 0;
        for (int i = 0; i <= node_num; ++ i) head[i] = -1;
    }
    
    inline void Add_Edge(int u, int v, ll w){
        edge[cnt].next = head[u];
        edge[cnt].to = v;
        edge[cnt].w = w;
        head[u] = cnt++;
    
        edge[cnt].next = head[v];
        edge[cnt].to = u;
        edge[cnt].w = 0;
        head[v] = cnt++;
    }
    
    
    inline bool Bfs(){
        for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
        dis[S] = 0;
        queue<int> q;
        q.push(S);
        while(!q.empty()){
            int u = q.front(); q.pop();
            for(int i = head[u]; i != -1; i = edge[i].next){
                int v = edge[i].to;
                if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                    dis[v] = dis[u] + 1;
                    q.push(v);
                }
            }
        }
        return dis[T] != -1;
    }
    
    inline ll dfs(int u, ll flow){
        if(u == T) return flow;
        ll del = flow;
        for(int i = cur[u]; i != -1; i = edge[i].next){
            cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
            int v = edge[i].to;
            if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
                ll ans = dfs(v, min(del, edge[i].w));//木桶原理
                edge[i].w -= ans;//正向弧减增广流量
                edge[i ^ 1].w += ans;//反向弧加增广流量
                del -= ans;//总流量减增广流量
                if(del == 0) break;//总流量为0则不继续增广
            }
        }
        return flow - del;//返回本次增广的流量
    }
    
    inline ll Dinic() {
        ll ans = 0;
        while(Bfs()) {
            for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
            ans += dfs(S, inf);
        }
        return ans;
    }
    /*
    0 0
    1 0
    3 3 (0,1) (0,2) (1,2)
    2 0
    5 7 (0,1) (0,2) (1,3) (1,2) (1,4) (2,3) (3,4)
    */
    int main(){
        while (~scanf("%d%d", &n, &m)){
            int u[m+1], v[m+1];
            for (int i = 0; i < m; i++)
                scanf(" (%d,%d)", &u[i], &v[i]);//, u[i]++, v[i]++;
            ll ans = inf;
            node_num = 2*n;
            for(int s=0;s<n;s++){
                for(int t=s+1;t<n;t++){
                    init();
                    S = s, T = t;
                    for(int i=0;i<n;i++) {
                        if (i == s || i == t) Add_Edge(i,i+n,inf);
                        else Add_Edge(i,i+n,1);
                    }
                    for(int i=0;i<m;i++) 
                        Add_Edge(u[i]+n,v[i],inf),Add_Edge(v[i]+n,u[i],inf);
                    ll temp = Dinic();
                    ans = min(ans,temp);
                }
            }
            if(ans == inf){
                if(n*(n-1)/2==m) printf("%d\n",n);
                else printf("0\n");
            }
            else printf("%d\n",ans);
        }
        return 0;
    }
    View Code

    Sabotage

    求最小割边集。其实我觉得求出残余网络中容量为0的就好了,不就是最小割么。(不知道对不对)

    (UPD20.9.11)跑满流的边只是可能为最小割边,反例: 1 ----(12)------2-------(12)--------3     假设S为1,T为3。如果满流的边是最小割的话,那么就是1-2和2-3,但是答案其实是1-2或者1-3。所以下述方法才是求最小割的正解!

    但是网上的做法好像都是先最大流之后dfs染色,染色中遇到容量为0的跳过。

    之后判断每条边左右节点的颜色是否不同就好了。

    #pragma GCC optimize(2)
    #include <bits/stdc++.h>
    using namespace std;
    #define pii pair<long long, int>
    typedef long long ll;
    
    const int maxn = 1000 + 10;
    const int maxm = 1e6 * 2 + 10;
    const ll inf = 1e14;
    const int mod = 1e9 + 7;
    
    struct Edge{
        int to;
        int next;
        ll w;
    } edge[maxm];
    
    int head[maxn], node_num;
    int cur[maxn];//当前弧优化数组
    int n, m, S, T, k;//点数,边数,源点,汇点
    int dis[maxn];//Bfs深度
    int cnt = 0;//边数
    bool vis[maxn];
    inline void init(){
        cnt = 0;
        for (int i = 0; i <= node_num; ++ i) head[i] = -1;
        memset(vis, 0, sizeof(vis));
    }
    
    inline void Add_Edge(int u, int v, ll w){
        edge[cnt].next = head[u];
        edge[cnt].to = v;
        edge[cnt].w = w;
        head[u] = cnt++;
    
        edge[cnt].next = head[v];
        edge[cnt].to = u;
        edge[cnt].w = 0;
        head[v] = cnt++;
    }
    
    
    inline bool Bfs(){
        for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
        dis[S] = 0;
        queue<int> q;
        q.push(S);
        while(!q.empty()){
            int u = q.front(); q.pop();
            for(int i = head[u]; i != -1; i = edge[i].next){
                int v = edge[i].to;
                if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                    dis[v] = dis[u] + 1;
                    q.push(v);
                }
            }
        }
        return dis[T] != -1;
    }
    
    inline ll dfs(int u, ll flow){
        if(u == T) return flow;
        ll del = flow;
        for(int i = cur[u]; i != -1; i = edge[i].next){
            cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
            int v = edge[i].to;
            if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
                ll ans = dfs(v, min(del, edge[i].w));//木桶原理
                edge[i].w -= ans;//正向弧减增广流量
                edge[i ^ 1].w += ans;//反向弧加增广流量
                del -= ans;//总流量减增广流量
                if(del == 0) break;//总流量为0则不继续增广
            }
        }
        return flow - del;//返回本次增广的流量
    }
    
    inline ll Dinic() {
        ll ans = 0;
        while(Bfs()) {
            for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
            ans += dfs(S, inf);
        }
        return ans;
    }
    
    void DFS(int u) {
        vis[u] = 1;//为1表示是S集的点
        for (int i = head[u]; ~i; i = edge[i].next) {
            int v = edge[i].to;
            if (vis[v] || !edge[i].w) continue;
            DFS(v);
        }
    }
    
    int main() {
        while (~scanf("%d%d",&n,&m)) {
            if (!n && !m) break;
            node_num = n;
            S = 1, T = 2;
            init();
            int x[m+1], y[m+1];
            for (int i = 1; i <= m; ++ i) {
                int u, v, w;
                scanf("%d%d%d",&u,&v,&w);
                Add_Edge(u, v, w), Add_Edge(v, u, w);
                x[i] = u, y[i] = v;
            }
            Dinic();
            DFS(S);
            for(int i = 1; i <= m; i++) {
                int u = x[i], v = y[i];
                if (vis[u] && !vis[v] || vis[v] && !vis[u])
                    printf("%d %d\n", u, v);
            }
            cout << endl;
        }
        return 0;
    }
    View Code

    Collectors Problem

    这道题我好像见过。这也算是一类题了吧(分配问题)。

    题目大意:Bob有一些贴纸,他可以和别人交换,他可以把自己独有的贴纸拿出去,也可以把重复的贴纸拿出去(有时候把独有的贴纸而不是重复的贴纸拿出去能换到更多贴纸)。

                  Bob的朋友也有一些贴纸,但是他们只会拿自己重复的贴纸和Bob换,而且换的是自己没有的贴纸。

                  求Bob最后最多能有多少种贴纸。

    解题思路

    题目意思很明确了。就算把重复的贴纸拿出去也不一定最优,贪心就不用尝试了。

    全局资源调配得使用网络流模型。

    建图方式:

    ①S点(看作是Bob)->所有物品:连一条边,cap是Bob持有贴纸数量。

    ②:所有朋友->所有物品:如果这个人持有的该贴纸数量>=2,连一条边,cap是贴纸数量-1。(原因是这些人只会把重复的贴纸拿出去)。

    ③:所有物品->所有朋友:如果这个人没有改物品,连一条边,cap=1,。(原因是这些人会接受自己没有的贴纸)

    ④:所有物品->T点:连一条边,cap=1,统计物品的种类。

    这样建图之后,所有物品可以看作Bob的总资产,这个总资产可以流进,也可以流出,在这基础上做一次最大流,就是结果了。

    #pragma GCC optimize(2)
    #include <bits/stdc++.h>
    using namespace std;
    #define pii pair<long long, int>
    typedef long long ll;
    
    const int maxn = 1000 + 10;
    const int maxm = 1e6 * 2 + 10;
    const ll inf = 1e14;
    const int mod = 1e9 + 7;
    
    struct Edge{
        int to;
        int next;
        ll w;
    } edge[maxm];
    
    int head[maxn], node_num;
    int cur[maxn];//当前弧优化数组
    int n, m, S, T, k;//点数,边数,源点,汇点
    int dis[maxn];//Bfs深度
    int tot = 0;//边数
    bool vis[maxn];
    inline void init(){
        tot = 0;
        for (int i = 0; i <= node_num; ++ i) head[i] = -1;
        memset(vis, 0, sizeof(vis));
    }
    
    inline void Add_Edge(int u, int v, ll w){
        edge[tot].next = head[u];
        edge[tot].to = v;
        edge[tot].w = w;
        head[u] = tot++;
    
        edge[tot].next = head[v];
        edge[tot].to = u;
        edge[tot].w = 0;
        head[v] = tot++;
    }
    
    
    inline bool Bfs(){
        for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
        dis[S] = 0;
        queue<int> q;
        q.push(S);
        while(!q.empty()){
            int u = q.front(); q.pop();
            for(int i = head[u]; i != -1; i = edge[i].next){
                int v = edge[i].to;
                if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                    dis[v] = dis[u] + 1;
                    q.push(v);
                }
            }
        }
        return dis[T] != -1;
    }
    
    inline ll dfs(int u, ll flow){
        if(u == T) return flow;
        ll del = flow;
        for(int i = cur[u]; i != -1; i = edge[i].next){
            cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
            int v = edge[i].to;
            if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
                ll ans = dfs(v, min(del, edge[i].w));//木桶原理
                edge[i].w -= ans;//正向弧减增广流量
                edge[i ^ 1].w += ans;//反向弧加增广流量
                del -= ans;//总流量减增广流量
                if(del == 0) break;//总流量为0则不继续增广
            }
        }
        return flow - del;//返回本次增广的流量
    }
    
    inline ll Dinic() {
        ll ans = 0;
        while(Bfs()) {
            for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
            ans += dfs(S, inf);
        }
        return ans;
    }
    
    void DFS(int u) {
        vis[u] = 1;//为1表示是S集的点
        for (int i = head[u]; ~i; i = edge[i].next) {
            int v = edge[i].to;
            if (vis[v] || !edge[i].w) continue;
            DFS(v);
        }
    }
    /*
    2
    2 5
    6 1 1 1 1 1 1
    3 1 2 2
    3 5
    4 1 2 1 1
    3 2 2 2
    5 1 3 4 4 3
    */
    int main() {
        int Ti; scanf("%d", &Ti);
        for (int Tii = 1; Tii <= Ti; ++ Tii) {
            scanf("%d %d", &n, &m);
            node_num = n+m+1;
            S = 0, T = n+m+1;
            init();
            int cnt[n+1][m+1] = {0};
            for (int u = 1, v; u <= n; u++) {
                int num; scanf("%d", &num);
                while (num--) {
                    scanf("%d", &v);
                    cnt[u][v]++;
                }
            }
            //1号是自己
            for (int i = 1; i <= m; i++)
                Add_Edge(S, i, cnt[1][i]);
            //2-n是其他人
            for (int i = 2; i <= n; i++) {
                for (int j = 1; j <= m; j++) {
                    if (!cnt[i][j]) Add_Edge(j, m+i, 1);
                    else Add_Edge(m+i, j, cnt[i][j]-1);
                }
            }
            for (int i = 1; i <= m; i++)
                Add_Edge(i, T, 1);
            int ans = Dinic();
            printf("Case #%d: %d\n", Tii, ans);
        }
    
        return 0;
    }
    View Code

    Matrix Decompressing

    思路:

      这么也想不到用网络流解决,这个模型很不错。假设这个矩阵的每一行是水管,每一列是水管,每行有出水口流到每一列。

      这样想比较好理解。然后每行的流量和每列的流量知道,就可以建图了。

      建图过程,每行对应一个点,每列对应1个点,每行都可以流到每列,所以他们之间有边。

      我们得假设他们是如何流向的,不如设从行流向列,那么添加源点,流向每行;添加汇点,被每列汇流。容量怎么设?我们要让每行都满流就行了,

      那么行之和就是源点到该行的容量,同理汇点也如此。但是每行流向每列呢?注意每个格子的元素必须在1~20之间,所以把容量设为20,别让它流太多了。

      注意到元素必须在1~20之间!!!那么这样增广路的话会出现有的完全0流怎么办?先将每个格子中的元素自减1,它的流下限总不会为负吧,计算完输出时再加回去不就行了。

    #pragma GCC optimize(2)
    #include <bits/stdc++.h>
    using namespace std;
    #define pii pair<long long, int>
    typedef long long ll;
    
    const int maxn = 1e6 + 10;
    const int maxm = 1e5 * 2 + 10;
    const ll inf = 1e18;
    const int mod = 1e9 + 7;
    
    
    struct Edge{
        int to;
        int next;
        ll w;
    } edge[maxn*2];
    
    int head[maxn], node_num;
    int cur[maxn];//当前弧优化数组
    int n, m, S, T, k;//点数,边数,源点,汇点
    int dis[maxn];//Bfs深度
    int cnt = 0;//边数
    bool vis[maxn];
    
    inline void init(){
        cnt = 0;
        for (int i = 0; i <= node_num; ++ i) head[i] = -1;
    }
    
    inline void Add_Edge(int u, int v, ll w){
        edge[cnt].next = head[u];
        edge[cnt].to = v;
        edge[cnt].w = w;
        head[u] = cnt++;
    
        edge[cnt].next = head[v];
        edge[cnt].to = u;
        edge[cnt].w = 0;
        head[v] = cnt++;
    
    }
    
    
    inline bool Bfs(){
        for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
        dis[S] = 0;
        queue<int> q;
        q.push(S);
        while(!q.empty()){
            int u = q.front(); q.pop();
            for(int i = head[u]; ~i ; i = edge[i].next){
                int v = edge[i].to;
                if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                    dis[v] = dis[u] + 1;
                    q.push(v);
                }
            }
        }
        return dis[T] != -1;
    }
    
    inline ll dfs(int u, ll flow) {
        if(u == T) return flow;
        ll del = flow;
        for(int i = cur[u]; ~i; i = edge[i].next){
            cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
            int v = edge[i].to;
            if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
                ll ans = dfs(v, min(del, edge[i].w));//木桶原理
                edge[i].w -= ans;//正向弧减增广流量
                edge[i ^ 1].w += ans;//反向弧加增广流量
                del -= ans;//总流量减增广流量
                if(del == 0) break;//总流量为0则不继续增广
            }
        }
        return flow - del;//返回本次增广的流量
    }
    
    inline ll Dinic() {
        ll ans = 0;
        while(Bfs()) {
            for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
            ans += dfs(S, inf);
        }
        return ans;
    }
    
    int main()
    {
        //freopen("in.txt","r",stdin);
        int Ti;
        scanf("%d",&Ti);
        for (int ti = 1; ti <= Ti; ++ ti) {
            init();
            cin>>n>>m;
            S = 0, T = 1+n+m;
            node_num = T;
    
            int a[30]={0}, b[30]={0}, c[30]={0}, d[30]={0};
            int rec[30][30] = {0};
            for (int i=1; i<=n; i++) cin>>a[i];
            for (int i=1; i<=m; i++) cin>>b[i];
            for (int i=1; i<=n; i++) c[i] = a[i]-a[i-1]-m;
            for (int i=1; i<=m; i++) d[i] = b[i]-b[i-1]-n;
    
            for (int i=1; i<=n; i++)
                Add_Edge(S,i,c[i]);
            for (int i=1; i<=m; i++)
                Add_Edge(n+i,T,d[i]);
            for (int i=1; i<=n; i++)
                for (int j=1; j<=m; j++)
                    Add_Edge(i,n+j,19), rec[i][j] = cnt-1;
    
            Dinic();
            cout << "Matrix " << ti << endl;
            for (int i = 1; i <= n; ++ i) {
                int j;
                for (j = 1; j <= m-1; ++ j) {
                    cout << edge[rec[i][j]].w + 1 << " ";
                }
                cout << edge[rec[i][m]].w +1<< endl;//edge[j].w+1 << endl;
            }
            cout << endl;
    
        }
        return 0;
    }
    View Code

     Frequency Hopping

    这道题真的很好。为了做这道题我特地去重新学习了一遍网络流。

    题意:问你是否在网络流中存在流量为C的流,如果不存在是否能修改一条边使得存在。

    思路:存在流量为C那么只需要最大流>=C就好了,因为最大流可以不跑满。

    所以possible的条件就是最大流.>= C

    通过最大流最小割定理我们知道,当流量是最大流的时候,割边流量肯定跑满,所以修改容量的边肯定是割边。

    所以我们枚举割边去修改

    (暴力会超时。所以这里要提两点优化:

      第一点是当跑最大流的时候一旦流量>=C那么直接跳出。

      第二点是从跑了一遍最大流的残余网络上再跑最大流。

      第一个点是比较好理解的就不讲了。第二点是因为Dinic算法是“阻塞流”,所以跑了一边dinic之后他会把一些道路给阻塞,就会使得增广的路减少从而达到优化时间的目的。

    如果每一条割边都不能满足就是not possible

    (最让我难受的是我的板子似乎不能满足这道题,改了一个上午还是超时。最后还是嫖了白书上的板子,以防日后会遇到相同的题目,用自己的板子写不出来。)

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<queue>
    using namespace std;
     
    #define maxn 110
    #define maxm 22010
    typedef long long ll;
    #define INF 0x3f3f3f3f
    #define cl(x,v); memset(x,v,sizeof(x));
     
    struct Edge{
        int from,to,cap,flow;
    };
     
    bool cmp(const Edge& a,const Edge& b)
    {
        return a.from < b.from||(a.from==b.from&&a.to<b.to);
    }
     
    struct Dinic{
        int n,m,s,t;
        vector<Edge>edges;
        vector<int>g[maxn];
        bool vis[maxn];
        int d[maxn];
        int cur[maxm];
        void init(int n)
        {
            this->n=n;
            for(int i=0;i<=n;i++)g[i].clear();
            edges.clear();
        }
        void AddEdge(int from,int to,int cap)
        {
            edges.push_back((Edge){from,to,cap,0});
            edges.push_back((Edge){to,from,0,0});
            m=edges.size();
            g[from].push_back(m-2);
            g[to].push_back(m-1);
        }
        bool BFS()
        {
            cl(vis,0);
            queue<int>q;
            q.push(s);
            d[s]=0;
            vis[s]=1;
            while(!q.empty())
            {
                int x=q.front();
                q.pop();
                for(int i=0;i<g[x].size();i++)
                {
                    Edge& e=edges[g[x][i]];
                    if(!vis[e.to]&&e.cap>e.flow)
                    {
                        vis[e.to]=1;
                        d[e.to]=d[x]+1;
                        q.push(e.to);
                    }
                }
            }
            return vis[t];
        }
        int DFS(int x,int a)
        {
            if(x==t||a==0)return a;
            int flow=0,f;
            for(int& i=cur[x];i<g[x].size();i++)
            {
                Edge& e=edges[g[x][i]];
                if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0)
                {
                    e.flow+=f;
                    edges[g[x][i]^1].flow-=f;
                    flow+=f;
                    a-=f;
                    if(a==0)break;
                }
            }
            return flow;
        }
        int Maxflow(int s,int t,int need)
        {
            this->s=s;
            this->t=t;
            int flow=0;
            while(BFS())
            {
                cl(cur,0);
                flow+=DFS(s,INF);
                if(flow>need)return flow;
            }
            return flow;
        }
        vector<int> Mincut()
        {
            BFS();
            vector<int>ans;
            for(int i=0;i<edges.size();i++)
            {
                Edge& e=edges[i];
                if(vis[e.from]&&!vis[e.to]&&e.cap>0)
                    ans.push_back(i);
            }
            return ans;
        }
        void Reduce()
        {
            for(int i=0;i<edges.size();i++)
                edges[i].cap-=edges[i].flow;
        }
        void ClearFlow()
        {
            for(int i=0;i<edges.size();i++)
                edges[i].flow=0;
        }
    };
     
    Dinic solver;
    int main()
    {
        int N,E,C,cas=0;
        while(scanf("%d%d%d",&N,&E,&C)!=EOF)
        {
            if(N==0)break;
            solver.init(N);
            int a,b,c;
            while(E--)
            {
                scanf("%d%d%d",&a,&b,&c);
                solver.AddEdge(a,b,c);
            }
            int flow=solver.Maxflow(1,N,INF);
            printf("Case %d: ",++cas);
            if(flow>=C)printf("possible\n");
            else
            {
                vector<int>cut=solver.Mincut();
                solver.Reduce();
                vector<Edge>ans;
                for(int i=0;i<cut.size();i++)
                {
                    Edge& e=solver.edges[cut[i]];
                    int temp=e.cap;
                    e.cap=C;
                    solver.ClearFlow();
                    if(flow+solver.Maxflow(1,N,C-flow)>=C)ans.push_back(e);
                    e.cap=temp;
                }
                if(ans.empty())printf("not possible\n");
                else
                {
                    sort(ans.begin(),ans.end(),cmp);
                    printf("possible option:(%d,%d)",ans[0].from,ans[0].to);
                    for(int i=1;i<ans.size();i++)
                        printf(",(%d,%d)",ans[i].from,ans[i].to);
                    printf("\n");
                }
            }
        }
        return 0;
    }
    View Code

     SAM I AM

     这道题跟上面那道挺像的。这道题让我解决了上面遗留的疑惑。同时也让我对最小割理解更为深刻。

    #pragma GCC optimize(2)
    #include <bits/stdc++.h>
    using namespace std;
    #define pii pair<long long, int>
    typedef long long ll;
    
    const int maxn = 3000 + 10;
    const int maxm = 1e6 * 2 + 100;
    const ll inf = 1e14;
    const int mod = 1e9 + 7;
    
    struct Edge{
        int to;
        int next;
        ll w;
    } edge[maxm];
    
    int head[maxn], node_num;
    int cur[maxn];//当前弧优化数组
    int n, m, S, T, num;//点数,边数,源点,汇点
    int dis[maxn];//Bfs深度
    int cnt = 0;//边数
    bool vis[maxn];
    inline void init(){
        cnt = 0;
        for (int i = 0; i <= node_num; ++ i) head[i] = -1;
        memset(vis, 0, sizeof(vis));
    }
    
    inline void Add_Edge(int u, int v, ll w){
        edge[cnt].next = head[u];
        edge[cnt].to = v;
        edge[cnt].w = w;
        head[u] = cnt++;
    
        edge[cnt].next = head[v];
        edge[cnt].to = u;
        edge[cnt].w = 0;
        head[v] = cnt++;
    }
    
    
    inline bool Bfs(){
        for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
        dis[S] = 0;
        queue<int> q;
        q.push(S);
        while(!q.empty()){
            int u = q.front(); q.pop();
            for(int i = head[u]; i != -1; i = edge[i].next){
                int v = edge[i].to;
                if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                    dis[v] = dis[u] + 1;
                    q.push(v);
                }
            }
        }
        return dis[T] != -1;
    }
    
    inline ll dfs(int u, ll flow){
        if(u == T) return flow;
        ll del = flow;
        for(int i = cur[u]; i != -1; i = edge[i].next){
            cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
            int v = edge[i].to;
            if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
                ll ans = dfs(v, min(del, edge[i].w));//木桶原理
                edge[i].w -= ans;//正向弧减增广流量
                edge[i ^ 1].w += ans;//反向弧加增广流量
                del -= ans;//总流量减增广流量
                if(del == 0) break;//总流量为0则不继续增广
            }
        }
        return flow - del;//返回本次增广的流量
    }
    
    inline ll Dinic() {
        ll ans = 0;
        while(Bfs()) {
            for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
            ans += dfs(S, inf);
        }
        return ans;
    }
    
    void DFS(int u) {
        vis[u] = 1;//为1表示是S集的点
        for (int i = head[u]; ~i; i = edge[i].next) {
            int v = edge[i].to;
            if (vis[v] || !edge[i].w) continue;
            DFS(v);
        }
    }
    
    int main() {
        while (~scanf("%d%d%d",&n,&m,&num)) {
            if (!n && !m && !num) break;
            node_num = n+m+1;
            S = 0, T = m+n+1;
            init();
            for (int i = 1; i <= n; ++ i) Add_Edge(S, i, 1);
            for (int i = 1; i <= m; ++ i) Add_Edge(i+n, T, 1);
            for (int i = 1,u,v; i <= num; ++ i)
                scanf("%d%d",&u,&v), Add_Edge(u, v+n, 1);
            printf("%lld ", Dinic());
            DFS(S);
            for (int i = 1; i <= n; ++ i)
                if (!vis[i]) printf("r%d ", i);
            for (int i = n+1; i <= m+n; ++ i)
                if (vis[i]) printf("c%d ", i-n);
            cout << endl;
        }
        return 0;
    }
    /*
    4 4 3
    1 1
    1 4
    3 2
    
    4 4 2
    1 1
    2 2
    
    0 0 0
    */
    View Code

    Power Transmission

    这道题算是比较综合的题目吧,多源多汇+点容量限制。还行。

    #pragma GCC optimize(2)
    #include <bits/stdc++.h>
    using namespace std;
    #define pii pair<long long, int>
    typedef long long ll;
    
    const int maxn = 3000 + 10;
    const int maxm = 1e6 * 2 + 100;
    const ll inf = 1e14;
    const int mod = 1e9 + 7;
    
    struct Edge{
        int to;
        int next;
        ll w;
    } edge[maxm];
    
    int head[maxn], node_num;
    int cur[maxn];//当前弧优化数组
    int n, m, S, T, num;//点数,边数,源点,汇点
    int dis[maxn];//Bfs深度
    int cnt = 0;//边数
    bool vis[maxn];
    inline void init(){
        cnt = 0;
        for (int i = 0; i <= node_num; ++ i) head[i] = -1;
        memset(vis, 0, sizeof(vis));
    }
    
    inline void Add_Edge(int u, int v, ll w){
        edge[cnt].next = head[u];
        edge[cnt].to = v;
        edge[cnt].w = w;
        head[u] = cnt++;
    
        edge[cnt].next = head[v];
        edge[cnt].to = u;
        edge[cnt].w = 0;
        head[v] = cnt++;
    }
    
    
    inline bool Bfs(){
        for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
        dis[S] = 0;
        queue<int> q;
        q.push(S);
        while(!q.empty()){
            int u = q.front(); q.pop();
            for(int i = head[u]; i != -1; i = edge[i].next){
                int v = edge[i].to;
                if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                    dis[v] = dis[u] + 1;
                    q.push(v);
                }
            }
        }
        return dis[T] != -1;
    }
    
    inline ll dfs(int u, ll flow){
        if(u == T) return flow;
        ll del = flow;
        for(int i = cur[u]; i != -1; i = edge[i].next){
            cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
            int v = edge[i].to;
            if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
                ll ans = dfs(v, min(del, edge[i].w));//木桶原理
                edge[i].w -= ans;//正向弧减增广流量
                edge[i ^ 1].w += ans;//反向弧加增广流量
                del -= ans;//总流量减增广流量
                if(del == 0) break;//总流量为0则不继续增广
            }
        }
        return flow - del;//返回本次增广的流量
    }
    
    inline ll Dinic() {
        ll ans = 0;
        while(Bfs()) {
            for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
            ans += dfs(S, inf);
        }
        return ans;
    }
    
    void DFS(int u) {
        vis[u] = 1;//为1表示是S集的点
        for (int i = head[u]; ~i; i = edge[i].next) {
            int v = edge[i].to;
            if (vis[v] || !edge[i].w) continue;
            DFS(v);
        }
    }
    
    int main() {
        //freopen("out.txt","w",stdout);
        while (~scanf("%d",&n)) {
            node_num = 2*n+1;
            S = 0, T = 2*n+1;
            init();
            for (int i = 1, w; i <= n; ++ i)
                scanf("%d", &w), Add_Edge(i, i+n, w);
            scanf("%d", &m);
            for (int i = 1,u,v,w; i <= m; ++ i)
                scanf("%d%d%d",&u, &v, &w), Add_Edge(u+n, v, w);
            int ed, st; scanf("%d%d", &st, &ed);
            for (int i = 1, tt; i <= st; ++ i)
                scanf("%d", &tt), Add_Edge(S, tt, inf);
            for (int i = 1, tt; i <= ed; ++ i)
                scanf("%d", &tt), Add_Edge(tt+n, T, inf);
            printf("%lld\n", Dinic());
        }
        return 0;
    }
    /*
    4 4 3
    1 1
    1 4
    3 2
    
    4 4 2
    1 1
    2 2
    
    0 0 0
    */
    View Code

    Down Went The Titanic

    多源多汇拆点。跟上一题差不多。只不过换成了矩阵的形式。

    #pragma GCC optimize(2)
    #include <bits/stdc++.h>
    using namespace std;
    #define pii pair<long long, int>
    typedef long long ll;
    
    const int maxn = 3000 + 10;
    const int maxm = 1e6 * 2 + 100;
    const ll inf = 1e14;
    const int mod = 1e9 + 7;
    
    struct Edge{
        int to;
        int next;
        ll w;
    } edge[maxm];
    
    int head[maxn], node_num;
    int cur[maxn];//当前弧优化数组
    int n, m, S, T, c;//点数,边数,源点,汇点
    int dis[maxn];//Bfs深度
    int cnt = 0;//边数
    bool vis[maxn];
    char G[35][35];
    inline void init(){
        cnt = 0;
        for (int i = 0; i <= node_num; ++ i) head[i] = -1;
        memset(vis, 0, sizeof(vis));
    }
    
    inline void Add_Edge(int u, int v, ll w){
        edge[cnt].next = head[u];
        edge[cnt].to = v;
        edge[cnt].w = w;
        head[u] = cnt++;
    
        edge[cnt].next = head[v];
        edge[cnt].to = u;
        edge[cnt].w = 0;
        head[v] = cnt++;
    }
    
    
    inline bool Bfs(){
        for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
        dis[S] = 0;
        queue<int> q;
        q.push(S);
        while(!q.empty()){
            int u = q.front(); q.pop();
            for(int i = head[u]; i != -1; i = edge[i].next){
                int v = edge[i].to;
                if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                    dis[v] = dis[u] + 1;
                    q.push(v);
                }
            }
        }
        return dis[T] != -1;
    }
    
    inline ll dfs(int u, ll flow){
        if(u == T) return flow;
        ll del = flow;
        for(int i = cur[u]; i != -1; i = edge[i].next){
            cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
            int v = edge[i].to;
            if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
                ll ans = dfs(v, min(del, edge[i].w));//木桶原理
                edge[i].w -= ans;//正向弧减增广流量
                edge[i ^ 1].w += ans;//反向弧加增广流量
                del -= ans;//总流量减增广流量
                if(del == 0) break;//总流量为0则不继续增广
            }
        }
        return flow - del;//返回本次增广的流量
    }
    
    inline ll Dinic() {
        ll ans = 0;
        while(Bfs()) {
            for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
            ans += dfs(S, inf);
        }
        return ans;
    }
    
    void DFS(int u) {
        vis[u] = 1;//为1表示是S集的点
        for (int i = head[u]; ~i; i = edge[i].next) {
            int v = edge[i].to;
            if (vis[v] || !edge[i].w) continue;
            DFS(v);
        }
    }
    int dic[4][2] ={0,1,0,-1,1,0,-1,0};
    int main() {
        //freopen("out.txt","w",stdout);
        while (~scanf("%d%d%d",&n,&m,&c)) {
            node_num = 2*n*m+1;
            S = 0, T = 2*n*m+1;
            init();
            for (int i = 1, w; i <= n; ++ i) scanf("%s", (G[i]+1));
            for (int i = 1; i <= n; ++ i) {
                for (int j = 1; j <= m; ++ j) {
                    int id = (i-1)*m+j;
                    if (G[i][j] == '*') Add_Edge(S, id, 1), Add_Edge(id, id+n*m, 1);
                    else if (G[i][j] == '~') continue;
                    else if (G[i][j] == '.') Add_Edge(id, id+n*m, 1);
                    else if (G[i][j] == '@') Add_Edge(id, id+n*m, inf);
                    else if (G[i][j] == '#') Add_Edge(id+n*m, T, c), Add_Edge(id, id+n*m, inf);
                    for (int k = 0; k < 4; ++ k) {
                        int tx = i+dic[k][0];
                        int ty = j+dic[k][1];
                        int id1 = (tx-1)*m+ty;
                        if (tx < 1 || tx > n || ty < 1 || ty > m || G[tx][ty] == '*' || G[tx][ty] == '~') continue;
                        Add_Edge(id+n*m, id1, inf);
                    }
                }
            }
    
            printf("%lld\n", Dinic());
        }
        return 0;
    }
    /*
    4 4 3
    1 1
    1 4
    3 2
    
    4 4 2
    1 1
    2 2
    
    0 0 0
    */
    View Code

    Objective: Berlin

    这题在洛谷上居然是黑题。也不是很难的样子。

    题意:给定一些航班,每个航班有人数,和起始终止时间,每次转机要花半小时,问限制时间内最多能有多少人从起始城市到终点城市

    思路:dinic最大流,将每个班机作为一个点,并且拆点成i'和i'',从i'->i''的边的限制为班机的人数,两个班机i和j如果i的终点是j的起点,且时间间隔至少为30分钟则在i''->j'建立边,最后是源点和汇点的边。

    #pragma GCC optimize(2)
    #include <bits/stdc++.h>
    using namespace std;
    #define pii pair<long long, int>
    typedef long long ll;
    
    const int maxn = 1e4 + 10;
    const int maxm = 1e6 * 2 + 100;
    const ll inf = 1e14;
    const int mod = 1e9 + 7;
    
    struct Edge{
        int to, next;
        ll w;
    } edge[maxm];
    
    int head[maxn], node_num;
    int cur[maxn];//当前弧优化数组
    int n, m, S, T, c;//点数,边数,源点,汇点
    int dis[maxn];//Bfs深度
    int cnt = 0;//边数
    //bool vis[maxn];
    
    inline void init(){
        cnt = 0;
        for (int i = 0; i <= node_num; ++ i) head[i] = -1;
    //    memset(vis, 0, sizeof(vis));
    }
    
    inline void Add_Edge(int u, int v, ll w){
        edge[cnt].next = head[u];
        edge[cnt].to = v;
        edge[cnt].w = w;
        head[u] = cnt++;
    
        edge[cnt].next = head[v];
        edge[cnt].to = u;
        edge[cnt].w = 0;
        head[v] = cnt++;
    }
    
    
    inline bool Bfs(){
        for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
        dis[S] = 0;
        queue<int> q;
        q.push(S);
        while(!q.empty()){
            int u = q.front(); q.pop();
            for(int i = head[u]; i != -1; i = edge[i].next){
                int v = edge[i].to;
                if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                    dis[v] = dis[u] + 1;
                    q.push(v);
                }
            }
        }
        return dis[T] != -1;
    }
    
    inline ll dfs(int u, ll flow){
        if(u == T) return flow;
        ll del = flow;
        for(int i = cur[u]; i != -1; i = edge[i].next){
            cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
            int v = edge[i].to;
            if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
                ll ans = dfs(v, min(del, edge[i].w));//木桶原理
                edge[i].w -= ans;//正向弧减增广流量
                edge[i ^ 1].w += ans;//反向弧加增广流量
                del -= ans;//总流量减增广流量
                if(del == 0) break;//总流量为0则不继续增广
            }
        }
        return flow - del;//返回本次增广的流量
    }
    
    inline ll Dinic() {
        ll ans = 0;
        while(Bfs()) {
            for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
            ans += dfs(S, inf);
        }
        return ans;
    }
    
    struct Plane {
        int u, v, num, stt, edt;
    }P[maxn];
    
    int main() {
        //freopen("out.txt","w",stdout);
        ios::sync_with_stdio(0);
        cin.tie(0);
        while (cin >> n) {
            map<string, int> mp;
            int hash_val = 0;
            string st, ed; cin >> st >> ed;
            mp[st] = hash_val++;
            mp[ed] = hash_val++;
    
            int end_time; cin >> end_time;
            end_time = end_time/100*60 + end_time%100;
            cin >> m;
            S = 0, T = 2*m+1;
            node_num = 2*m+1;
            init();
            for (int i = 1; i <= m; ++ i) {
                string u, v;
                int num, edt, stt;
                cin >> u >> v >> num >> stt >> edt;
                if (!mp[u]) mp[u] = hash_val++;
                if (!mp[v]) mp[v] = hash_val++;
                stt = stt/100*60 + stt%100;
                edt = edt/100*60 + edt%100;
                P[i] = {mp[u], mp[v], num, stt, edt};
            }
            for (int i = 1; i <= m; ++ i) {
                Add_Edge(i, i+m, P[i].num);
                if (P[i].u == mp[st]) Add_Edge(S, i, inf);
                if (P[i].v == mp[ed] && P[i].edt <= end_time) Add_Edge(i+m, T, inf);
                for (int j = 1; j <= m; ++j) {
                    if (i == j) continue;
                    if (P[i].v == P[j].u && P[j].stt - P[i].edt >= 30)
                        Add_Edge(i+m, j, inf);
                }
            }
            printf("%lld\n", Dinic());
        }
        return 0;
    }
    /*
    4
    lisbon berlin
    1500
    9
    lisbon london 6 1000 1100
    london lisbon 6 1130 1230
    lisbon paris 5 1000 1100
    paris lisbon 4 1130 1230
    london paris 1 1130 1300
    london berlin 2 1340 1510
    berlin london 2 1300 1430
    paris berlin 10 1330 1500
    berlin paris 9 1300 1430
    */
    View Code

    Hard Life

     这道题是最大密度子图的模板题。

    第一种方式

    #pragma GCC optimize(2)
    #include <bits/stdc++.h>
    using namespace std;
    #define pii pair<long long, int>
    typedef long long ll;
    
    const int maxn = 2011;
    const int maxm = 1e6 * 2 + 100;
    const double inf = 1e9;
    const int mod = 1e9 + 7;
    
    struct Edge{
        int to, next;
        double w;
    } edge[maxn*4];
    
    int head[maxn], node_num;
    int cur[maxn];//当前弧优化数组
    int n, m, S, T, c;//点数,边数,源点,汇点
    int dis[maxn];//Bfs深度
    int cnt = 0;//边数
    int vis[maxn];
    int tx[maxn], ty[maxn];
    
    
    inline void Add_Edge(int u, int v, double w){
        edge[cnt].next = head[u];
        edge[cnt].to = v;
        edge[cnt].w = w;
        head[u] = cnt++;
    
        edge[cnt].next = head[v];
        edge[cnt].to = u;
        edge[cnt].w = 0;
        head[v] = cnt++;
    }
    
    
    inline bool Bfs(){
        for(int i = 0; i <= node_num; ++i) dis[i] = -1;
        dis[S] = 0;
        queue<int> q;
        q.push(S);
        while(!q.empty()){
            int u = q.front(); q.pop();
            for(int i = head[u]; i != -1; i = edge[i].next){
                int v = edge[i].to;
                if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                    dis[v] = dis[u] + 1;
                    q.push(v);
                }
            }
        }
        return dis[T] != -1;
    }
    
    inline double dfs(int u, double flow){
        if(u == T) return flow;
        double del = flow;
        for(int i = cur[u]; i != -1; i = edge[i].next){
            cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
            int v = edge[i].to;
            if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
                double ans = dfs(v, min(del, edge[i].w));//木桶原理
                edge[i].w -= ans;//正向弧减增广流量
                edge[i ^ 1].w += ans;//反向弧加增广流量
                del -= ans;//总流量减增广流量
                if(del == 0) break;//总流量为0则不继续增广
            }
        }
        return flow - del;//返回本次增广的流量
    }
    
    inline double Dinic() {
        double ans = 0;
        while(Bfs()) {
            for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
            ans += dfs(S, inf);
        }
        return ans;
    }
    
    void build(double mid) {
        cnt = 0;
        memset(head, -1, sizeof(head));
        for (int i = 1; i <= m; ++ i)
            Add_Edge(n+i, tx[i], inf), Add_Edge(n+i, ty[i], inf);
        for (int i = 1; i <= m; ++ i) Add_Edge(S, n+i, 1.0);
        for (int i = 1; i <= n; ++ i) Add_Edge(i, T, mid);
    }
    
    void mark(int u) {
        vis[u] = 1;
        for (int i = head[u]; ~i; i = edge[i].next) {
            int v = edge[i].to;
            if (!edge[i].w || vis[v]) continue;
            mark(v);
        }
    }
    
    int main() {
        //freopen("out.txt","w",stdout);
        while (~scanf("%d%d", &n, &m)) {
            if (m == 0) { printf("1\n1\n"); continue;}
            for (int i = 1; i <= m; ++ i) scanf("%d%d", &tx[i], &ty[i]);
            S = 0, T = n+m+1, node_num=T;
            double L=0,R=m,M, eps=1.0/n/n;
            while (R - L > eps) {
                build(M = (L+R)/2);
                if ((double)m-Dinic() > 1e-8) L = M;
                else R = M;
            }
            //cout << L << endl;
            build(L);
            Dinic();
            memset(vis, 0, sizeof(vis));
            mark(S);
            int ans=0;
            for(int i=1;i<=n;i++) if(vis[i]) ans++;
            printf("%d\n",ans);
            for(int i=1;i<=n;i++) if(vis[i]) printf("%d\n",i);
        }
        return 0;
    }
    /*
    5 6
    1 5
    5 4
    4 2
    2 5
    1 2
    3 1
    */
    View Code

     附第二种方法的代码。

    View Code

    餐巾计划问题

    这个写得挺好的
    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    typedef pair<ll, int> pii;
    
    struct Edge {
        int u, v;
        ll flow, cap, cost;
        int next;
    };
    
    const int MAXN = 5000, MAXM = 50000;
    const ll INF = 0x3f3f3f3f3f3f3f3fLL;
    
    int e_ptr = 1, S, T, head[MAXN+10]; Edge E[(MAXM+10)<<1];
    ll dist[MAXN+10], MaxFlow, MinCost, delta;
    int inq[MAXN+10], done[MAXN+10], vis[MAXN+10];
    
    void AddEdge(int u, int v, ll cap, ll cost) {
        E[++e_ptr] = (Edge) { u, v, 0, cap, cost, head[u] }; head[u] = e_ptr;
        E[++e_ptr] = (Edge) { v, u, 0,  0, -cost, head[v] }; head[v] = e_ptr;
    }
    
    void Reduce() {
        for(int i = 2; i <= e_ptr; i++)
            E[i].cost += dist[E[i].v] - dist[E[i].u];
        delta += dist[S];
    }
    
    bool BellmanFord() {
        queue<int> Q;
        memset(dist, 0x3f, sizeof(dist));
        dist[T] = 0; Q.push(T); inq[T] = true;
        while(!Q.empty()) {
            int u = Q.front(); Q.pop(); inq[u] = false;
            for(int j=head[u]; j; j=E[j].next) {
                int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost;
                if(f < c && dist[v] > dist[u] + len) {
                    dist[v] = dist[u] + len;
                    if(!inq[v]) {
                        inq[v] = true;
                        Q.push(v);
                    }
                }
            }
        }
        return dist[S] != INF;
    }
    
    bool Dijkstra() {
        memset(dist, 0x3f, sizeof(dist));
        memset(vis, 0, sizeof(vis));
        priority_queue<pii,vector<pii>,greater<pii> > pq;
        dist[T] = 0; pq.push({dist[T], T});
        while(!pq.empty()) {
            int u = pq.top().second; pq.pop();
            if(vis[u]) continue;
            vis[u] = 1;
            for(int j=head[u]; j; j=E[j].next) {
                int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost;
                if(f < c && dist[v] > dist[u] + len) {
                    dist[v] = dist[u] + len;
                    pq.push({dist[v],v});
                }
            }
        }
        return dist[S] != INF;
    }
    
    ll DFS(int u, ll flow) {
        if(u == T || flow == 0) return flow;
        vis[u] = true; // differ from dinic
        ll res = flow;
        for(int j=head[u]; j; j=E[j].next) {
            int v = E[j].v; ll f = E[j].flow, c = E[j].cap, len = E[j].cost;
            if(!vis[v] && f < c && len == 0) { // not `dist[v] == dist[u]` ! they do not equal !
                ll tmp = DFS(v, min(res, c-f)); // len = 0 <=> on the shortest path
                E[j].flow += tmp;
                E[j^1].flow -= tmp;
                res -= tmp;
            }
        }
        return flow - res;
    }
    
    void Augment() {
        ll CurFlow = 0;
        while(memset(vis, 0, sizeof(vis)),
            (CurFlow = DFS(S, INF))) {
            MaxFlow += CurFlow;
            MinCost += CurFlow * delta;
        }
    }
    
    void PrimalDual() {
        if(!BellmanFord()) return;
        Reduce(); Augment();
        while(Dijkstra()) {
            Reduce(); Augment();
        }
    }
    
    int main() {
        int N; scanf("%d", &N);
        S = 0, T = 2*N+1;
        for(int i = 1, x; i <= N; i++) {
            scanf("%d", &x);
            AddEdge(S, i, x, 0); //每天晚上收到x条脏毛巾
            AddEdge(i+N, T, x, 0); //每天早上需要送出x条干净的毛巾
        }
        int p, m, f, s, n;
        scanf("%d%d%d%d%d", &p, &m, &f, &n, &s);
        for (int i = 1; i <= N; ++ i) {
            if (i + 1 <= N) AddEdge(i, i+1, INF, 0); //每天晚上可以把剩下的毛巾留到明天晚上
            if (i + m <= N) AddEdge(i, i+m+N, INF, f);  //把晚上的毛巾送去洗到m天后的早上
            if (i + n <= N) AddEdge(i, i+n+N, INF, s);//把晚上的毛巾送去洗到n天后的早上
            AddEdge(S, i+n, INF, p); //每天早上可以买毛巾
        }
        PrimalDual();
        printf("%lld", MinCost);
        return 0;
    }
    View Code

    方格取数加强版

    这道题太裸了。唯一需要注意的点就是需要连一条用作经过的边。

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    typedef pair<ll, int> pii;
    
    struct Edge {
        int u, v;
        ll flow, cap, cost;
        int next;
    };
    
    const int MAXN = 5000, MAXM = 50000;
    const ll INF = 0x3f3f3f3f3f3f3f3fLL;
    
    int e_ptr = 1, S, T, head[MAXN+10]; Edge E[(MAXM+10)<<1];
    ll dist[MAXN+10], MaxFlow, MinCost, delta;
    int inq[MAXN+10], done[MAXN+10], vis[MAXN+10];
    
    void AddEdge(int u, int v, ll cap, ll cost) {
        E[++e_ptr] = (Edge) { u, v, 0, cap, cost, head[u] }; head[u] = e_ptr;
        E[++e_ptr] = (Edge) { v, u, 0,  0, -cost, head[v] }; head[v] = e_ptr;
    }
    
    void Reduce() {
        for(int i = 2; i <= e_ptr; i++)
            E[i].cost += dist[E[i].v] - dist[E[i].u];
        delta += dist[S];
    }
    
    bool BellmanFord() {
        queue<int> Q;
        memset(dist, 0x3f, sizeof(dist));
        dist[T] = 0; Q.push(T); inq[T] = true;
        while(!Q.empty()) {
            int u = Q.front(); Q.pop(); inq[u] = false;
            for(int j=head[u]; j; j=E[j].next) {
                int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost;
                if(f < c && dist[v] > dist[u] + len) {
                    dist[v] = dist[u] + len;
                    if(!inq[v]) {
                        inq[v] = true;
                        Q.push(v);
                    }
                }
            }
        }
        return dist[S] != INF;
    }
    
    bool Dijkstra() {
        memset(dist, 0x3f, sizeof(dist));
        memset(vis, 0, sizeof(vis));
        priority_queue<pii,vector<pii>,greater<pii> > pq;
        dist[T] = 0; pq.push({dist[T], T});
        while(!pq.empty()) {
            int u = pq.top().second; pq.pop();
            if(vis[u]) continue;
            vis[u] = 1;
            for(int j=head[u]; j; j=E[j].next) {
                int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost;
                if(f < c && dist[v] > dist[u] + len) {
                    dist[v] = dist[u] + len;
                    pq.push({dist[v],v});
                }
            }
        }
        return dist[S] != INF;
    }
    
    ll DFS(int u, ll flow) {
        if(u == T || flow == 0) return flow;
        vis[u] = true; // differ from dinic
        ll res = flow;
        for(int j=head[u]; j; j=E[j].next) {
            int v = E[j].v; ll f = E[j].flow, c = E[j].cap, len = E[j].cost;
            if(!vis[v] && f < c && len == 0) { // not `dist[v] == dist[u]` ! they do not equal !
                ll tmp = DFS(v, min(res, c-f)); // len = 0 <=> on the shortest path
                E[j].flow += tmp;
                E[j^1].flow -= tmp;
                res -= tmp;
            }
        }
        return flow - res;
    }
    
    void Augment() {
        ll CurFlow = 0;
        while(memset(vis, 0, sizeof(vis)),
            (CurFlow = DFS(S, INF))) {
            MaxFlow += CurFlow;
            MinCost += CurFlow * delta;
        }
    }
    
    void PrimalDual() {
        if(!BellmanFord()) return;
        Reduce(); Augment();
        while(Dijkstra()) {
            Reduce(); Augment();
        }
    }
    int g[55][55];
    int dic[2][2] = {1,0,0,1};
    int main() {
        int n, k; scanf("%d%d", &n, &k);
        S = 0, T = 2*n*n+1;
        for(int i = 1; i <= n; i++)
            for (int j = 1; j <= n; ++ j)
                scanf("%d", &g[i][j]);
        AddEdge(S, 1, k, 0), AddEdge(n*n*2, T, k, 0);
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= n; ++j) {
                int id = (i-1)*n+j;
                AddEdge(id, id+n*n, 1, -g[i][j]), AddEdge(id, id+n*n, INF, 0);
                for (int k = 0; k < 2; ++ k) {
                    int tx = i+dic[k][0], ty = j+dic[k][1];
                    if (tx > n || ty > n) continue;
                    int _id = (tx-1)*n+ty;
                    AddEdge(id+n*n, _id, INF, 0);
                }
            }
        }
        PrimalDual();
        printf("%lld", -MinCost);
        return 0;
    }
    View Code

    [SCOI2007]修车

    这道题的建图十分的难想。大家都知道要拆点,但是怎么拆就是个问题。这个算是涨个知识,知道建图还能这么建。

    之后只需要车向时间段连固定代价即可。

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    typedef pair<ll, int> pii;
    
    struct Edge {
        int u, v;
        ll flow, cap, cost;
        int next;
    };
    
    const int MAXN = 5000, MAXM = 50000;
    const ll INF = 0x3f3f3f3f3f3f3f3fLL;
    
    int e_ptr = 1, S, T, head[MAXN+10]; Edge E[(MAXM+10)<<1];
    ll dist[MAXN+10], MaxFlow, MinCost, delta;
    int inq[MAXN+10], done[MAXN+10], vis[MAXN+10];
    
    void AddEdge(int u, int v, ll cap, ll cost) {
        E[++e_ptr] = (Edge) { u, v, 0, cap, cost, head[u] }; head[u] = e_ptr;
        E[++e_ptr] = (Edge) { v, u, 0,  0, -cost, head[v] }; head[v] = e_ptr;
    }
    
    void Reduce() {
        for(int i = 2; i <= e_ptr; i++)
            E[i].cost += dist[E[i].v] - dist[E[i].u];
        delta += dist[S];
    }
    
    bool BellmanFord() {
        queue<int> Q;
        memset(dist, 0x3f, sizeof(dist));
        dist[T] = 0; Q.push(T); inq[T] = true;
        while(!Q.empty()) {
            int u = Q.front(); Q.pop(); inq[u] = false;
            for(int j=head[u]; j; j=E[j].next) {
                int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost;
                if(f < c && dist[v] > dist[u] + len) {
                    dist[v] = dist[u] + len;
                    if(!inq[v]) {
                        inq[v] = true;
                        Q.push(v);
                    }
                }
            }
        }
        return dist[S] != INF;
    }
    
    bool Dijkstra() {
        memset(dist, 0x3f, sizeof(dist));
        memset(vis, 0, sizeof(vis));
        priority_queue<pii,vector<pii>,greater<pii> > pq;
        dist[T] = 0; pq.push({dist[T], T});
        while(!pq.empty()) {
            int u = pq.top().second; pq.pop();
            if(vis[u]) continue;
            vis[u] = 1;
            for(int j=head[u]; j; j=E[j].next) {
                int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost;
                if(f < c && dist[v] > dist[u] + len) {
                    dist[v] = dist[u] + len;
                    pq.push({dist[v],v});
                }
            }
        }
        return dist[S] != INF;
    }
    
    ll DFS(int u, ll flow) {
        if(u == T || flow == 0) return flow;
        vis[u] = true; // differ from dinic
        ll res = flow;
        for(int j=head[u]; j; j=E[j].next) {
            int v = E[j].v; ll f = E[j].flow, c = E[j].cap, len = E[j].cost;
            if(!vis[v] && f < c && len == 0) { // not `dist[v] == dist[u]` ! they do not equal !
                ll tmp = DFS(v, min(res, c-f)); // len = 0 <=> on the shortest path
                E[j].flow += tmp;
                E[j^1].flow -= tmp;
                res -= tmp;
            }
        }
        return flow - res;
    }
    
    void Augment() {
        ll CurFlow = 0;
        while(memset(vis, 0, sizeof(vis)),
            (CurFlow = DFS(S, INF))) {
            MaxFlow += CurFlow;
            MinCost += CurFlow * delta;
        }
    }
    
    void PrimalDual() {
        if(!BellmanFord()) return;
        Reduce(); Augment();
        while(Dijkstra()) {
            Reduce(); Augment();
        }
    }
    int cost[65][10];
    int main() {
        int n, m; scanf("%d%d", &m, &n); //n车,m人
        S = 0, T = m*n+n+1;
        for (int i = 1; i <= m; ++ i)
            for (int j = 1; j <= n; ++ j)
                AddEdge((i-1)*n+j+n, T, 1, 0);
        for (int i = 1; i <= n; ++ i) { //最外层是车
            AddEdge(S, i, 1, 0);
            for (int j = 1; j <= m; ++ j) { //这一层是人
                int temp; scanf("%d", &temp);
                for (int k = 1; k <= n; ++ k) //这一层是枚举时间段
                    AddEdge(i, n+(j-1)*n+k, 1, k*temp);
            }
        }
        PrimalDual();
        printf("%.2f\n", 1.0*MinCost/n);
        return 0;
    }
    View Code

    [NOI2012]美食节

    修车的加强版。区别就是每款车都有个数了,所以时间段建点会很多,会导致爆时间。

    具体的优化就是需要根据每次哪个人倒数第几次修车被跑满了,就动态加入新的时间段。

    附上嫖自洛谷的代码。

    #include <iostream>
    #include <iomanip>
    #include <cstring>
    #include <map>
    #include <queue>
    #define inf 2147483646
    #define N 10000
    using namespace std;
    
    struct ed{
        int u,w,next,f;
    }e[1000000];
    int g[1000][2000],a[2000];
    int n,m,st=1,ans,cost,sm,fir[30000],c[30000],d[30000];
    int vis[30000],sp[30000];
    queue<int> q; bool v[30000]; 
    map<int,int> ha;
    
    void add(int x,int y,int w,int f)
    {
        e[++st].u=y; e[st].next=fir[x]; e[fir[x]=st].w=w; e[st].f=f;
        e[++st].u=x; e[st].next=fir[y]; e[fir[y]=st].w=0; e[st].f=-f;
    }
    
    bool spfa()
    {
        for (int i=0;i<=N;i++) d[i]=inf/2,c[i]=fir[i],v[i]=0;
        q.push(0); v[0]=1; d[0]=0;
        while (!q.empty())
        {
            int k=q.front(); q.pop();  v[k]=0;
            for (int i=fir[k];i;i=e[i].next){
                int u=e[i].u,w=e[i].f;
                if (d[u]>d[k]+w&&e[i].w){
                    d[u]=d[k]+w; if (!v[u]) v[u]=1,q.push(u);
                }
            }
        } 
        return (d[N]<inf/2);
    }
    
    int dfs(int p,int now)
    {
        if (p==N){v[N]=1; return now;}
        int mw=0;  v[p]=1;
        for (int i=fir[p];i;i=e[i].next)
        {
            int u=e[i].u,w=e[i].f; 
            if (d[u]==d[p]+w&&e[i].w&&(!v[u]||u==N))
            if (mw=dfs(u,min(e[i].w,now)))
            {
                e[i].w-=mw; e[i^1].w+=mw; 
                cost+=mw*w; return mw;
            }
        }
    }
    
    void dinic()
    {
        while (spfa()) {
             ans+=dfs(0,inf);
             for (int i=fir[N];i;i=e[i].next){
                 int u=e[i].u,w=e[i].w;
                 if (w&&!vis[u]) {
                     vis[u]=1; int co=ha[u]; sp[co]++;
                     add(++sm,N,1,0); ha[sm]=co;
                     for (int i=1;i<=n;i++) add(i,sm,1,sp[co]*g[i][co]);
                 }
             }
        }
    }
    
    int main()
    {
        cin>>n>>m; int sum=0;
        for (int i=1;i<=n;i++) cin>>a[i],sum+=a[i];
        for (int i=1;i<=n;i++) 
        for (int j=1;j<=m;j++) cin>>g[i][j];
        
        for (int i=1;i<=n;i++) add(0,i,a[i],0);
        sm=n;
        //for (int k=1;k<=n;k++) 时间K(总数不为n了) 
        for (int j=1;j<=m;j++) {//厨师j 
            add(++sm,N,1,0); ha[sm]=j; sp[j]=1;
            for (int i=1;i<=n;i++) add(i,sm,1,g[i][j]); //菜i 
        }
        dinic();
        cout<<cost<<endl; 
    }
    View Code

    [SDOI2009]晨跑

    路线不会相交,所以每个点只能走一遍。直接拆点,出点和入点连费用为0容量为1的边。

    对于每条有向边,起点出点连终点入点,容量为1,费用为输入费用。

    关于代码中S = 1+n(1号点的出点),T = n(n号点的入点),是因为S和T是可以重复经过的。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long ll;
    typedef pair<ll, int> pii;
    
    struct Edge {
        int u, v;
        ll flow, cap, cost;
        int next;
    };
    
    const int MAXN = 5000, MAXM = 50000;
    const ll LLINF = 0x3f3f3f3f3f3f3f3fLL;
    int n, m;
    int e_ptr = 1, S, T, head[MAXN+10]; Edge E[(MAXM+10)<<1];
    ll dist[MAXN+10], MaxFlow, MinCost, delta;
    int inq[MAXN+10], done[MAXN+10], vis[MAXN+10];
    
    void AddEdge(int u, int v, ll cap, ll cost) {
        E[++e_ptr] = (Edge) { u, v, 0, cap, cost, head[u] }; head[u] = e_ptr;
        E[++e_ptr] = (Edge) { v, u, 0,  0, -cost, head[v] }; head[v] = e_ptr;
    }
    
    void Reduce() {
        for(int i = 2; i <= e_ptr; i++)
            E[i].cost += dist[E[i].v] - dist[E[i].u];
        delta += dist[S];
    }
    
    bool BellmanFord() {
        queue<int> Q;
        memset(dist, 0x3f, sizeof(dist));
        dist[T] = 0; Q.push(T); inq[T] = true;
        while(!Q.empty()) {
            int u = Q.front(); Q.pop(); inq[u] = false;
            for(int j=head[u]; j; j=E[j].next) {
                int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost;
                if(f < c && dist[v] > dist[u] + len) {
                    dist[v] = dist[u] + len;
                    if(!inq[v]) {
                        inq[v] = true;
                        Q.push(v);
                    }
                }
            }
        }
        return dist[S] != LLINF;
    }
    
    bool Dijkstra() {
        memset(dist, 0x3f, sizeof(dist));
        memset(vis, 0, sizeof(vis));
        priority_queue<pii,vector<pii>,greater<pii> > pq;
        dist[T] = 0; pq.push({dist[T], T});
        while(!pq.empty()) {
            int u = pq.top().second; pq.pop();
            if(vis[u]) continue;
            vis[u] = 1;
            for(int j=head[u]; j; j=E[j].next) {
                int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost;
                if(f < c && dist[v] > dist[u] + len) {
                    dist[v] = dist[u] + len;
                    pq.push({dist[v],v});
                }
            }
        }
        return dist[S] != LLINF;
    }
    
    ll DFS(int u, ll flow) {
        if(u == T || flow == 0) return flow;
        vis[u] = true; // differ from dinic
        ll res = flow;
        for(int j=head[u]; j; j=E[j].next) {
            int v = E[j].v; ll f = E[j].flow, c = E[j].cap, len = E[j].cost;
            if(!vis[v] && f < c && len == 0) { // not `dist[v] == dist[u]` ! they do not equal !
                ll tmp = DFS(v, min(res, c-f)); // len = 0 <=> on the shortest path
                E[j].flow += tmp;
                E[j^1].flow -= tmp;
                res -= tmp;
            }
        }
        return flow - res;
    }
    
    void Augment() {
        ll CurFlow = 0;
        while(memset(vis, 0, sizeof(vis)),
            (CurFlow = DFS(S, LLINF))) {
            MaxFlow += CurFlow;
            MinCost += CurFlow * delta;
        }
    }
    
    void PrimalDual() {
        if(!BellmanFord()) return;
        Reduce(); Augment();
        while(Dijkstra()) {
            Reduce(); Augment();
        }
    }
    
    int main() {
        scanf("%d%d", &n, &m);
        S = n+1, T = n;
        for (int i = 1; i <= n; ++ i)
            AddEdge(i, i+n, 1, 0);
        for(int i=1; i<=m; i++) {
            int u, v, cost;
            scanf("%d%d%d", &u, &v, &cost);
            AddEdge(u+n, v, 1, cost);
        }
        PrimalDual();
        printf("%lld %lld", MaxFlow, MinCost);
        return 0;
    }
    View Code

    [SDOI2010]星际竞速

    第一道最小路径覆盖的费用流的题目。

    再费用流中,最小路径覆盖就不能像在最大流中一样了。

    但是背后的思想都是一样的。

    在最大流中的最小路径覆盖的方法是:先将n个点拆点为ii′。然后由源点向所有的i点连一条容量为1的边,再由所有的i'点向汇点连一条容量为1的边,对于每条边u−>v,由uv'连一条容量为1的边,跑一遍最大流后,n减去最大流就是最小路径覆盖。

    为什么可以这样做呢?可以发现,上面其实是一个二分图,最大流实际上就是最大匹配数。在这里可以发现,匹配中每连一条边,路径的数量就减1

    所以路径数 = n - 最大流

     算是给自己长个见识把。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long ll;
    typedef pair<ll, int> pii;
    
    struct Edge {
        int u, v;
        ll flow, cap, cost;
        int next;
    };
    
    const int MAXN = 5000, MAXM = 50000;
    const ll LLINF = 0x3f3f3f3f3f3f3f3fLL;
    
    int e_ptr = 1, S, T, n, m, head[MAXN+10]; Edge E[(MAXM+10)<<1];
    ll dist[MAXN+10], MaxFlow, MinCost, delta;
    int inq[MAXN+10], done[MAXN+10], vis[MAXN+10];
    
    void AddEdge(int u, int v, ll cap, ll cost) {
        E[++e_ptr] = (Edge) { u, v, 0, cap, cost, head[u] }; head[u] = e_ptr;
        E[++e_ptr] = (Edge) { v, u, 0,  0, -cost, head[v] }; head[v] = e_ptr;
    }
    
    void Reduce() {
        for(int i = 2; i <= e_ptr; i++)
            E[i].cost += dist[E[i].v] - dist[E[i].u];
        delta += dist[S];
    }
    
    bool BellmanFord() {
        queue<int> Q;
        memset(dist, 0x3f, sizeof(dist));
        dist[T] = 0; Q.push(T); inq[T] = true;
        while(!Q.empty()) {
            int u = Q.front(); Q.pop(); inq[u] = false;
            for(int j=head[u]; j; j=E[j].next) {
                int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost;
                if(f < c && dist[v] > dist[u] + len) {
                    dist[v] = dist[u] + len;
                    if(!inq[v]) {
                        inq[v] = true;
                        Q.push(v);
                    }
                }
            }
        }
        return dist[S] != LLINF;
    }
    
    bool Dijkstra() {
        memset(dist, 0x3f, sizeof(dist));
        memset(vis, 0, sizeof(vis));
        priority_queue<pii,vector<pii>,greater<pii> > pq;
        dist[T] = 0; pq.push({dist[T], T});
        while(!pq.empty()) {
            int u = pq.top().second; pq.pop();
            if(vis[u]) continue;
            vis[u] = 1;
            for(int j=head[u]; j; j=E[j].next) {
                int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost;
                if(f < c && dist[v] > dist[u] + len) {
                    dist[v] = dist[u] + len;
                    pq.push({dist[v],v});
                }
            }
        }
        return dist[S] != LLINF;
    }
    
    ll DFS(int u, ll flow) {
        if(u == T || flow == 0) return flow;
        vis[u] = true; // differ from dinic
        ll res = flow;
        for(int j=head[u]; j; j=E[j].next) {
            int v = E[j].v; ll f = E[j].flow, c = E[j].cap, len = E[j].cost;
            if(!vis[v] && f < c && len == 0) { // not `dist[v] == dist[u]` ! they do not equal !
                ll tmp = DFS(v, min(res, c-f)); // len = 0 <=> on the shortest path
                E[j].flow += tmp;
                E[j^1].flow -= tmp;
                res -= tmp;
            }
        }
        return flow - res;
    }
    
    void Augment() {
        ll CurFlow = 0;
        while(memset(vis, 0, sizeof(vis)),
            (CurFlow = DFS(S, LLINF))) {
            MaxFlow += CurFlow;
            MinCost += CurFlow * delta;
        }
    }
    
    void PrimalDual() {
        if(!BellmanFord()) return;
        Reduce(); Augment();
        while(Dijkstra()) {
            Reduce(); Augment();
        }
    }
    
    int main() {
        int u, v, cap, cost;
        scanf("%d%d", &n, &m);
        S = 0, T = n+n+1;
        for (int i = 1; i <= n; i ++) {
            int temp; scanf("%d", &temp);
            AddEdge(S, i+n, 1, temp);
            AddEdge(S, i, 1, 0);
            AddEdge(i+n, T, 1, 0);
        }
    
        for (int i = 1, u, v, c; i <= m; i ++) {
            scanf("%d%d%d", &u, &v, &c);
            if (u > v) swap(u, v);
            AddEdge(u, v+n, 1, c);
        }
    
        PrimalDual();
        printf("%lld\n", MinCost);
        return 0;
    }
    View Code

    [HAOI2010]订货

    比较裸。但是我最近拆点做多了,啥都拆点。不过这道题倒是不用。直接连就好了
    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    typedef pair<ll, int> pii;
    
    struct Edge {
        int u, v;
        ll flow, cap, cost;
        int next;
    };
    
    const int MAXN = 5000, MAXM = 50000;
    const ll LLINF = 0x3f3f3f3f3f3f3f3fLL;
    int n, m;
    int e_ptr = 1, S, T, head[MAXN+10]; Edge E[(MAXM+10)<<1];
    ll dist[MAXN+10], MaxFlow, MinCost, delta;
    int inq[MAXN+10], done[MAXN+10], vis[MAXN+10];
    
    void AddEdge(int u, int v, ll cap, ll cost) {
        E[++e_ptr] = (Edge) { u, v, 0, cap, cost, head[u] }; head[u] = e_ptr;
        E[++e_ptr] = (Edge) { v, u, 0,  0, -cost, head[v] }; head[v] = e_ptr;
    }
    
    void Reduce() {
        for(int i = 2; i <= e_ptr; i++)
            E[i].cost += dist[E[i].v] - dist[E[i].u];
        delta += dist[S];
    }
    
    bool BellmanFord() {
        queue<int> Q;
        memset(dist, 0x3f, sizeof(dist));
        dist[T] = 0; Q.push(T); inq[T] = true;
        while(!Q.empty()) {
            int u = Q.front(); Q.pop(); inq[u] = false;
            for(int j=head[u]; j; j=E[j].next) {
                int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost;
                if(f < c && dist[v] > dist[u] + len) {
                    dist[v] = dist[u] + len;
                    if(!inq[v]) {
                        inq[v] = true;
                        Q.push(v);
                    }
                }
            }
        }
        return dist[S] != LLINF;
    }
    
    bool Dijkstra() {
        memset(dist, 0x3f, sizeof(dist));
        memset(vis, 0, sizeof(vis));
        priority_queue<pii,vector<pii>,greater<pii> > pq;
        dist[T] = 0; pq.push({dist[T], T});
        while(!pq.empty()) {
            int u = pq.top().second; pq.pop();
            if(vis[u]) continue;
            vis[u] = 1;
            for(int j=head[u]; j; j=E[j].next) {
                int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost;
                if(f < c && dist[v] > dist[u] + len) {
                    dist[v] = dist[u] + len;
                    pq.push({dist[v],v});
                }
            }
        }
        return dist[S] != LLINF;
    }
    
    ll DFS(int u, ll flow) {
        if(u == T || flow == 0) return flow;
        vis[u] = true; // differ from dinic
        ll res = flow;
        for(int j=head[u]; j; j=E[j].next) {
            int v = E[j].v; ll f = E[j].flow, c = E[j].cap, len = E[j].cost;
            if(!vis[v] && f < c && len == 0) { // not `dist[v] == dist[u]` ! they do not equal !
                ll tmp = DFS(v, min(res, c-f)); // len = 0 <=> on the shortest path
                E[j].flow += tmp;
                E[j^1].flow -= tmp;
                res -= tmp;
            }
        }
        return flow - res;
    }
    
    void Augment() {
        ll CurFlow = 0;
        while(memset(vis, 0, sizeof(vis)),
            (CurFlow = DFS(S, LLINF))) {
            MaxFlow += CurFlow;
            MinCost += CurFlow * delta;
        }
    }
    
    void PrimalDual() {
        if(!BellmanFord()) return;
        Reduce(); Augment();
        while(Dijkstra()) {
            Reduce(); Augment();
        }
    }
    
    int main() {
        int store;
        scanf("%d%d%d", &n, &m, &store);
        S = 0, T = 2*n+1;
        for (int i = 1; i <= n; ++ i) {
            int temp; scanf("%d", &temp);
            AddEdge(i, T, temp, 0);
    //        AddEdge(i, i+n, store, 0);
            if (i < n) AddEdge(i, i+1, store, m);
        }
        for (int i = 1; i <= n; ++ i) {
            int temp; scanf("%d", &temp);
            AddEdge(S, i, LLINF, temp);
        }
        PrimalDual();
        printf("%lld\n", MinCost);
        return 0;
    }
    View Code

    [ZJOI2010]网络扩容

    这道题我以为会是像上面那道UVA上的网络扩容一样的,之后就在求最小割,之后枚举边,之后扩容跑MFMC。
    但是其实这道题可以很简单就可以,原边的费用都是0,之后跑一次最大流。
    清除原来的图,之后在建一次图,在原图基础上再加一条容量无限费用为wi的。同时新开一个T = n+1,n连向T容量原先的最大流+k费用为0.
    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    typedef pair<ll, int> pii;
    
    struct Edge {
        int u, v;
        ll flow, cap, cost;
        int next;
    };
    
    const int MAXN = 5000+10, MAXM = 50000;
    const ll LLINF = 0x3f3f3f3f3f3f3f3fLL;
    int n, m, k;
    int e_ptr = 1, S, T, head[MAXN]; Edge E[(MAXM)<<1];
    ll dist[MAXN], MaxFlow, MinCost, delta;
    int inq[MAXN], done[MAXN], vis[MAXN];
    int color[MAXN];
    void AddEdge(int u, int v, ll cap, ll cost) {
        E[++e_ptr] = (Edge) { u, v, 0, cap, cost, head[u] }; head[u] = e_ptr;
        E[++e_ptr] = (Edge) { v, u, 0,  0, -cost, head[v] }; head[v] = e_ptr;
    }
    
    void Reduce() {
        for(int i = 2; i <= e_ptr; i++)
            E[i].cost += dist[E[i].v] - dist[E[i].u];
        delta += dist[S];
    }
    
    bool BellmanFord() {
        queue<int> Q;
        memset(dist, 0x3f, sizeof(dist));
        dist[T] = 0; Q.push(T); inq[T] = true;
        while(!Q.empty()) {
            int u = Q.front(); Q.pop(); inq[u] = false;
            for(int j=head[u]; j; j=E[j].next) {
                int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost;
                if(f < c && dist[v] > dist[u] + len) {
                    dist[v] = dist[u] + len;
                    if(!inq[v]) {
                        inq[v] = true;
                        Q.push(v);
                    }
                }
            }
        }
        return dist[S] != LLINF;
    }
    
    bool Dijkstra() {
        memset(dist, 0x3f, sizeof(dist));
        memset(vis, 0, sizeof(vis));
        priority_queue<pii,vector<pii>,greater<pii> > pq;
        dist[T] = 0; pq.push({dist[T], T});
        while(!pq.empty()) {
            int u = pq.top().second; pq.pop();
            if(vis[u]) continue;
            vis[u] = 1;
            for(int j=head[u]; j; j=E[j].next) {
                int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost;
                if(f < c && dist[v] > dist[u] + len) {
                    dist[v] = dist[u] + len;
                    pq.push({dist[v],v});
                }
            }
        }
        return dist[S] != LLINF;
    }
    
    ll DFS(int u, ll flow) {
        if(u == T || flow == 0) return flow;
        vis[u] = true; // differ from dinic
        ll res = flow;
        for(int j=head[u]; j; j=E[j].next) {
            int v = E[j].v; ll f = E[j].flow, c = E[j].cap, len = E[j].cost;
            if(!vis[v] && f < c && len == 0) { // not `dist[v] == dist[u]` ! they do not equal !
                ll tmp = DFS(v, min(res, c-f)); // len = 0 <=> on the shortest path
                E[j].flow += tmp;
                E[j^1].flow -= tmp;
                res -= tmp;
            }
        }
        return flow - res;
    }
    
    void Augment() {
        ll CurFlow = 0;
        while(memset(vis, 0, sizeof(vis)),
            (CurFlow = DFS(S, LLINF))) {
            MaxFlow += CurFlow;
            MinCost += CurFlow * delta;
        }
    }
    
    void PrimalDual() {
        if(!BellmanFord()) return;
        Reduce(); Augment();
        while(Dijkstra()) {
            Reduce(); Augment();
        }
    }
    struct record {
        int u, v;
        ll w, c;
    }rec[MAXN];
    int main() {
        scanf("%d%d%d", &n, &m, &k);
        S = 1, T = n;
        for (int i = 1,u,v,c; i <= m; ++ i) {
            scanf("%d%d%d%d", &rec[i].u, &rec[i].v, &rec[i].c, &rec[i].w);
            AddEdge(rec[i].u, rec[i].v, rec[i].c, 0);
        }
        PrimalDual();
        printf("%lld ", MaxFlow);
        T = n+1; memset(head, 0, sizeof(head)), e_ptr = 1;
        AddEdge(n, T, MaxFlow+k, 0);
        for (int i = 1,u,v,c; i <= m; ++ i) {
            AddEdge(rec[i].u, rec[i].v, rec[i].c, 0);
            AddEdge(rec[i].u, rec[i].v, LLINF, rec[i].w);
        }
        ll rec_MinCost = MinCost;
        PrimalDual();
        printf("%lld\n", MinCost);
        return 0;
    }
    View Code

     P2770 航空路线问题

    这道题的DFS遍历非常值得学习,因为必然是从 i + n 到 j 。

    所以我们进入的点必然是 i + n 点, 出去的点必然是 j ,所以我们就直接跳到 j +n 就代表经过 j 点。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long ll;
    typedef pair<ll, int> pii;
    
    struct Edge {
        int u, v;
        ll flow, cap, cost;
        int next;
    };
    
    const int MAXN = 5000, MAXM = 50000;
    const ll LLINF = 0x3f3f3f3f3f3f3f3fLL;
    
    int e_ptr = 1, S, T, n, m, head[MAXN+10]; Edge E[(MAXM+10)<<1];
    ll dist[MAXN+10], MaxFlow, MinCost, delta;
    int inq[MAXN+10], done[MAXN+10], vis[MAXN+10];
    
    void AddEdge(int u, int v, ll cap, ll cost) {
        E[++e_ptr] = (Edge) { u, v, 0, cap, cost, head[u] }; head[u] = e_ptr;
        E[++e_ptr] = (Edge) { v, u, 0,  0, -cost, head[v] }; head[v] = e_ptr;
    }
    
    void Reduce() {
        for(int i = 2; i <= e_ptr; i++)
            E[i].cost += dist[E[i].v] - dist[E[i].u];
        delta += dist[S];
    }
    
    bool BellmanFord() {
        queue<int> Q;
        memset(dist, 0x3f, sizeof(dist));
        dist[T] = 0; Q.push(T); inq[T] = true;
        while(!Q.empty()) {
            int u = Q.front(); Q.pop(); inq[u] = false;
            for(int j=head[u]; j; j=E[j].next) {
                int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost;
                if(f < c && dist[v] > dist[u] + len) {
                    dist[v] = dist[u] + len;
                    if(!inq[v]) {
                        inq[v] = true;
                        Q.push(v);
                    }
                }
            }
        }
        return dist[S] != LLINF;
    }
    
    bool Dijkstra() {
        memset(dist, 0x3f, sizeof(dist));
        memset(vis, 0, sizeof(vis));
        priority_queue<pii,vector<pii>,greater<pii> > pq;
        dist[T] = 0; pq.push({dist[T], T});
        while(!pq.empty()) {
            int u = pq.top().second; pq.pop();
            if(vis[u]) continue;
            vis[u] = 1;
            for(int j=head[u]; j; j=E[j].next) {
                int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost;
                if(f < c && dist[v] > dist[u] + len) {
                    dist[v] = dist[u] + len;
                    pq.push({dist[v],v});
                }
            }
        }
        return dist[S] != LLINF;
    }
    
    ll DFS(int u, ll flow) {
        if(u == T || flow == 0) return flow;
        vis[u] = true; // differ from dinic
        ll res = flow;
        for(int j=head[u]; j; j=E[j].next) {
            int v = E[j].v; ll f = E[j].flow, c = E[j].cap, len = E[j].cost;
            if(!vis[v] && f < c && len == 0) { // not `dist[v] == dist[u]` ! they do not equal !
                ll tmp = DFS(v, min(res, c-f)); // len = 0 <=> on the shortest path
                E[j].flow += tmp;
                E[j^1].flow -= tmp;
                res -= tmp;
            }
        }
        return flow - res;
    }
    
    void Augment() {
        ll CurFlow = 0;
        while(memset(vis, 0, sizeof(vis)),
            (CurFlow = DFS(S, LLINF))) {
            MaxFlow += CurFlow;
            MinCost += CurFlow * delta;
        }
    }
    
    void PrimalDual() {
        if(!BellmanFord()) return;
        Reduce(); Augment();
        while(Dijkstra()) {
            Reduce(); Augment();
        }
    }
    
    map<string, int> name;
    map<int, string> num;
    int color[MAXN];
    
    void DFS1(int u) {
        color[u]=1;
        cout << num[u-n] << endl;
        for(int i=head[u];i;i=E[i].next)
            if(E[i].v<=n&&E[i].cap > 0 && E[i].cap == E[i].flow) {
                DFS1(E[i].v+n);
                break;
            }
    }
    void DFS2(int u) {
        for(int i=head[u];i;i=E[i].next)
            if(E[i].v<=n&&!color[E[i].v+n]&& E[i].cap == E[i].flow)
            DFS2(E[i].v+n);
        cout<<num[u-n]<<endl;
    }
    
    int main() {
        cin >> n >> m;
        S = 1, T = n+n;
        int flag = 0;
        AddEdge(S, 1+n, 2, -1), AddEdge(n, T, 2, -1);
        for (int i = 1; i <= n; ++ i) {
            string city; cin >> city;
            name[city] = i, num[i] = city;
            if (i == 1 || i == n) continue;
            AddEdge(name[city], name[city]+n, 1, -1);
        }
        for (int i = 1; i <= m; ++ i) {
            string u, v; cin >> u >> v;
            if (name[u] > name[v]) swap(u, v);
            AddEdge(name[u]+n, name[v], 1, 0);
            if (name[u] == 1 && name[v] == n) flag = 1;
        }
        PrimalDual();
        if (MaxFlow != 2 && !flag) printf("No Solution!\n");
        else if (MaxFlow == 1 && flag) {
            cout << 2 << endl;
            cout << num[1] << endl << num[n] << endl << num[1] << endl;
        }
        else {
            cout << -MinCost-2 << endl;
            DFS1(n+1), DFS2(1+n);
        }
    
    
        return 0;
    }
    View Code

     P3159 [CQOI2012]交换棋子

    这道题主要需要注意的是这里是交换而不是移动,所以这里拆点方式就会很特别。

     

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    typedef pair<ll, int> pii;
    
    struct Edge {
        int u, v;
        ll flow, cap, cost;
        int next;
    };
    
    const int MAXN = 5000+10, MAXM = 50000;
    const ll LLINF = 0x3f3f3f3f3f3f3f3fLL;
    int n, m, k;
    int e_ptr = 1, S, T, head[MAXN]; Edge E[(MAXM)<<1];
    ll dist[MAXN], MaxFlow, MinCost, delta;
    int inq[MAXN], done[MAXN], vis[MAXN];
    
    void AddEdge(int u, int v, ll cap, ll cost) {
        E[++e_ptr] = (Edge) { u, v, 0, cap, cost, head[u] }; head[u] = e_ptr;
        E[++e_ptr] = (Edge) { v, u, 0,  0, -cost, head[v] }; head[v] = e_ptr;
    }
    
    void Reduce() {
        for(int i = 2; i <= e_ptr; i++)
            E[i].cost += dist[E[i].v] - dist[E[i].u];
        delta += dist[S];
    }
    
    bool BellmanFord() {
        queue<int> Q;
        memset(dist, 0x3f, sizeof(dist));
        dist[T] = 0; Q.push(T); inq[T] = true;
        while(!Q.empty()) {
            int u = Q.front(); Q.pop(); inq[u] = false;
            for(int j=head[u]; j; j=E[j].next) {
                int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost;
                if(f < c && dist[v] > dist[u] + len) {
                    dist[v] = dist[u] + len;
                    if(!inq[v]) {
                        inq[v] = true;
                        Q.push(v);
                    }
                }
            }
        }
        return dist[S] != LLINF;
    }
    
    bool Dijkstra() {
        memset(dist, 0x3f, sizeof(dist));
        memset(vis, 0, sizeof(vis));
        priority_queue<pii,vector<pii>,greater<pii> > pq;
        dist[T] = 0; pq.push({dist[T], T});
        while(!pq.empty()) {
            int u = pq.top().second; pq.pop();
            if(vis[u]) continue;
            vis[u] = 1;
            for(int j=head[u]; j; j=E[j].next) {
                int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost;
                if(f < c && dist[v] > dist[u] + len) {
                    dist[v] = dist[u] + len;
                    pq.push({dist[v],v});
                }
            }
        }
        return dist[S] != LLINF;
    }
    
    ll DFS(int u, ll flow) {
        if(u == T || flow == 0) return flow;
        vis[u] = true; // differ from dinic
        ll res = flow;
        for(int j=head[u]; j; j=E[j].next) {
            int v = E[j].v; ll f = E[j].flow, c = E[j].cap, len = E[j].cost;
            if(!vis[v] && f < c && len == 0) { // not `dist[v] == dist[u]` ! they do not equal !
                ll tmp = DFS(v, min(res, c-f)); // len = 0 <=> on the shortest path
                E[j].flow += tmp;
                E[j^1].flow -= tmp;
                res -= tmp;
            }
        }
        return flow - res;
    }
    
    void Augment() {
        ll CurFlow = 0;
        while(memset(vis, 0, sizeof(vis)),
            (CurFlow = DFS(S, LLINF))) {
            MaxFlow += CurFlow;
            MinCost += CurFlow * delta;
        }
    }
    
    void PrimalDual() {
        if(!BellmanFord()) return;
        Reduce(); Augment();
        while(Dijkstra()) {
            Reduce(); Augment();
        }
    }
    char st[25][25];
    char ed[25][25];
    char times[25][25];
    int dic[8][2] = {1,0, 0,1, -1,0, 0,-1, 1,1, 1,-1, -1,1, -1,-1};
    int main() {
        cin >> n >> m;
        S = 0, T = n*m*3+1;
        int cnt1 = 0, cnt2 = 0;
        for (int i = 1; i <= n; ++ i)
            for (int j = 1; j <= m; ++ j)
                cin >> st[i][j], cnt1 += st[i][j]-'0';
        for (int i = 1; i <= n; ++ i)
            for (int j = 1; j <= m; ++ j)
                cin >> ed[i][j], cnt2 += ed[i][j]-'0';
        for (int i = 1; i <= n; ++ i)
            for (int j = 1; j <= m; ++ j)
                cin >> times[i][j];
        // 不加就是pre , + n*m就是mid,  + 2*n*m就是aft
        for (int i = 1; i <= n; ++ i) {
            for (int j = 1; j <= m; ++ j) {
                int id = (i-1)*m+j;
                int limit = times[i][j]-'0';
                if (st[i][j] == '1') AddEdge(S, id+n*m, 1, 0);
                if (ed[i][j] == '1') AddEdge(id+n*m, T, 1, 0);
                if (st[i][j] == '0' && ed[i][j] == '1')
                    AddEdge(id, id+n*m, (limit+1)/2, 0), AddEdge(id+n*m, id+2*n*m, limit/2, 0);
                else if (st[i][j] == '1' && ed[i][j] == '0')
                    AddEdge(id, id+n*m, limit/2, 0), AddEdge(id+n*m, id+2*n*m, (limit+1)/2, 0);
                else
                    AddEdge(id, id+n*m, limit/2, 0), AddEdge(id+n*m, id+2*n*m, limit/2, 0);
                for (int k = 0; k < 8; ++ k) {
                    int tx = i+dic[k][0], ty = j+dic[k][1];
                    if (tx < 1 || ty < 1 || tx > n || ty > m) continue;
                    int id1 = (tx-1)*m + ty;
                    AddEdge(id+2*n*m, id1, LLINF, 1);
                }
            }
        }
        PrimalDual();
        if (cnt1 != cnt2 || MaxFlow != cnt1) cout << -1 << endl;
        else cout << MinCost << endl;
        return 0;
    }
    View Code

    P3356 火星探险问题

    主要就是路径输出,看来我已经掌握了路径输出的要点了。

    这道题因为没有用最大费用导致debug了好久。好累。

    今天把讨厌的人删了,还是挺舒服的。

    建图不是很难,我都想到了

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    typedef pair<ll, int> pii;
    
    struct Edge {
        int u, v;
        ll flow, cap, cost;
        int next;
    };
    
    const int MAXN = 5000+10, MAXM = 50000;
    const ll LLINF = 0x3f3f3f3f3f3f3f3fLL;
    int n, m, k;
    int e_ptr = 1, S, T, head[MAXN]; Edge E[(MAXM)<<1];
    ll dist[MAXN], MaxFlow, MinCost, delta;
    int inq[MAXN], done[MAXN], vis[MAXN];
    
    void AddEdge(int u, int v, ll cap, ll cost) {
        E[++e_ptr] = (Edge) { u, v, 0, cap, cost, head[u] }; head[u] = e_ptr;
        E[++e_ptr] = (Edge) { v, u, 0,  0, -cost, head[v] }; head[v] = e_ptr;
    }
    
    void Reduce() {
        for(int i = 2; i <= e_ptr; i++)
            E[i].cost += dist[E[i].v] - dist[E[i].u];
        delta += dist[S];
    }
    
    bool BellmanFord() {
        queue<int> Q;
        memset(dist, 0x3f, sizeof(dist));
        dist[T] = 0; Q.push(T); inq[T] = true;
        while(!Q.empty()) {
            int u = Q.front(); Q.pop(); inq[u] = false;
            for(int j=head[u]; j; j=E[j].next) {
                int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost;
                if(f < c && dist[v] > dist[u] + len) {
                    dist[v] = dist[u] + len;
                    if(!inq[v]) {
                        inq[v] = true;
                        Q.push(v);
                    }
                }
            }
        }
        return dist[S] != LLINF;
    }
    
    bool Dijkstra() {
        memset(dist, 0x3f, sizeof(dist));
        memset(vis, 0, sizeof(vis));
        priority_queue<pii,vector<pii>,greater<pii> > pq;
        dist[T] = 0; pq.push({dist[T], T});
        while(!pq.empty()) {
            int u = pq.top().second; pq.pop();
            if(vis[u]) continue;
            vis[u] = 1;
            for(int j=head[u]; j; j=E[j].next) {
                int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost;
                if(f < c && dist[v] > dist[u] + len) {
                    dist[v] = dist[u] + len;
                    pq.push({dist[v],v});
                }
            }
        }
        return dist[S] != LLINF;
    }
    
    ll DFS(int u, ll flow) {
        if(u == T || flow == 0) return flow;
        vis[u] = true; // differ from dinic
        ll res = flow;
        for(int j=head[u]; j; j=E[j].next) {
            int v = E[j].v; ll f = E[j].flow, c = E[j].cap, len = E[j].cost;
            if(!vis[v] && f < c && len == 0) { // not `dist[v] == dist[u]` ! they do not equal !
                ll tmp = DFS(v, min(res, c-f)); // len = 0 <=> on the shortest path
                E[j].flow += tmp;
                E[j^1].flow -= tmp;
                res -= tmp;
            }
        }
        return flow - res;
    }
    
    void Augment() {
        ll CurFlow = 0;
        while(memset(vis, 0, sizeof(vis)),
            (CurFlow = DFS(S, LLINF))) {
            MaxFlow += CurFlow;
            MinCost += CurFlow * delta;
        }
    }
    
    void PrimalDual() {
        if(!BellmanFord()) return;
        Reduce(); Augment();
        while(Dijkstra()) {
            Reduce(); Augment();
        }
    }
    int g[36][36];
    int dic[2][2] = {1,0, 0,1};
    
    void FindPath(int tms, int u) {
        for (int i = head[u]; i; i = E[i].next) {
            int v = E[i].v;
            if (v == u-n*m) continue;
            if (v == T || v == S) continue;
            if (!E[i].flow) continue;
            E[i].flow--;
            int pos = (v == u-n*m+1);
            cout << tms << " " << pos << endl;
            FindPath(tms, v+n*m);
            break;
        }
    }
    
    int main() {
        int num; scanf("%d", &num);
        scanf("%d%d", &m, &n);
        S = 0, T = n*m*2+1;
        for (int i = 1; i <= n; ++ i)
            for (int j = 1; j <= m; ++ j)
                scanf("%d", &g[i][j]);
        AddEdge(S, 1, num, 0), AddEdge(n*m*2, T, LLINF, 0);
        for (int i = 1; i <= n; ++ i) {
            for (int j = 1; j <= m; ++ j) {
                int id = (i-1)*m+j;
                AddEdge(id, id+n*m, LLINF, 0);
                if (g[i][j] == 1) continue ;
                if (g[i][j] == 2) AddEdge(id, id+n*m, 1, -1);
                for (int k = 0; k < 2; ++ k) {
                    int tx = i+dic[k][0], ty = j+dic[k][1];
                    int _id = (tx-1)*m+ty;
                    if (ty > m || tx > n || g[tx][ty] == 1) continue;
                    AddEdge(id+n*m, _id, LLINF, 0);
                }
            }
        }
        PrimalDual();
        for (int i = 1; i <= MaxFlow; ++ i)
            FindPath(i, 1+n*m);
        return 0;
    }
    View Code

    P3440 [POI2006]SZK-Schools

    看数据范围就知道很暴力。直接将200范围全部拆点,暴力连就好了。跑出来最大流不为n就输出NIE。

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    typedef pair<ll, int> pii;
    
    struct Edge {
        int u, v;
        ll flow, cap, cost;
        int next;
    };
    
    const int MAXN = 5000+10, MAXM = 50000;
    const ll LLINF = 0x3f3f3f3f3f3f3f3fLL;
    int n, m, k;
    int e_ptr = 1, S, T, head[MAXN]; Edge E[(MAXM)<<1];
    ll dist[MAXN], MaxFlow, MinCost, delta;
    int inq[MAXN], done[MAXN], vis[MAXN];
    
    void AddEdge(int u, int v, ll cap, ll cost) {
        E[++e_ptr] = (Edge) { u, v, 0, cap, cost, head[u] }; head[u] = e_ptr;
        E[++e_ptr] = (Edge) { v, u, 0,  0, -cost, head[v] }; head[v] = e_ptr;
    }
    
    void Reduce() {
        for(int i = 2; i <= e_ptr; i++)
            E[i].cost += dist[E[i].v] - dist[E[i].u];
        delta += dist[S];
    }
    
    bool BellmanFord() {
        queue<int> Q;
        memset(dist, 0x3f, sizeof(dist));
        dist[T] = 0; Q.push(T); inq[T] = true;
        while(!Q.empty()) {
            int u = Q.front(); Q.pop(); inq[u] = false;
            for(int j=head[u]; j; j=E[j].next) {
                int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost;
                if(f < c && dist[v] > dist[u] + len) {
                    dist[v] = dist[u] + len;
                    if(!inq[v]) {
                        inq[v] = true;
                        Q.push(v);
                    }
                }
            }
        }
        return dist[S] != LLINF;
    }
    
    bool Dijkstra() {
        memset(dist, 0x3f, sizeof(dist));
        memset(vis, 0, sizeof(vis));
        priority_queue<pii,vector<pii>,greater<pii> > pq;
        dist[T] = 0; pq.push({dist[T], T});
        while(!pq.empty()) {
            int u = pq.top().second; pq.pop();
            if(vis[u]) continue;
            vis[u] = 1;
            for(int j=head[u]; j; j=E[j].next) {
                int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost;
                if(f < c && dist[v] > dist[u] + len) {
                    dist[v] = dist[u] + len;
                    pq.push({dist[v],v});
                }
            }
        }
        return dist[S] != LLINF;
    }
    
    ll DFS(int u, ll flow) {
        if(u == T || flow == 0) return flow;
        vis[u] = true; // differ from dinic
        ll res = flow;
        for(int j=head[u]; j; j=E[j].next) {
            int v = E[j].v; ll f = E[j].flow, c = E[j].cap, len = E[j].cost;
            if(!vis[v] && f < c && len == 0) { // not `dist[v] == dist[u]` ! they do not equal !
                ll tmp = DFS(v, min(res, c-f)); // len = 0 <=> on the shortest path
                E[j].flow += tmp;
                E[j^1].flow -= tmp;
                res -= tmp;
            }
        }
        return flow - res;
    }
    
    void Augment() {
        ll CurFlow = 0;
        while(memset(vis, 0, sizeof(vis)),
            (CurFlow = DFS(S, LLINF))) {
            MaxFlow += CurFlow;
            MinCost += CurFlow * delta;
        }
    }
    
    void PrimalDual() {
        if(!BellmanFord()) return;
        Reduce(); Augment();
        while(Dijkstra()) {
            Reduce(); Augment();
        }
    }
    
    struct Node {
    
    } sch[205];
    
    int main() {
        int n; scanf("%d", &n);
        S = 0, T = 200+n+1;
        for (int i = 1; i <= n; ++ i) {
            int mi, ai, bi, ki;
            scanf("%d %d %d %d", &mi, &ai, &bi, &ki);
            for (int j = ai; j <= bi; ++ j)
                AddEdge(i, n+j, 1, ki*abs(j-mi));
            AddEdge(S, i, 1, 0);
        }
        for (int i = 1; i <= 200; ++ i) AddEdge(n+i, T, 1, 0);
        PrimalDual();
        if (MaxFlow != n) cout << "NIE" << endl;
        else cout << MinCost << endl;
        return 0;
    }
    View Code

    P3705 [SDOI2017]新生舞会

     我的原始对偶板子好像不是很适合求这种分数规划费用流的题目,这算是给个板子吧。思想是不难的。

    #include<bits/stdc++.h>
    using namespace std;
    int read() {
        char cc = getchar(); int cn = 0, flus = 1;
        while(cc < '0' || cc > '9') {  if( cc == '-' ) flus = -flus;  cc = getchar();  }
        while(cc >= '0' && cc <= '9')  cn = cn * 10 + cc - '0', cc = getchar();
        return cn * flus;
    }
    const int M = 2e5 + 5 ; 
    const int N = 200 + 5 ; 
    #define inf 123456789
    #define eps 1e-8
    #define rep( i, s, t ) for( register int i = s; i <= t; ++ i )
    #define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
    struct E {
        int to, next, w;
        double f ; 
    } e[M * 2]; 
    int head[N * 2], cur[N * 2], cnt, n, mark[N * 2], vis[N * 2], s, t ;
    double dis[N * 2], Ans, a[N][N], b[N][N] ;
    void add( int x, int y, int z, double f ) {
        e[++ cnt] = (E){ y, head[x], z, f }, head[x] = cnt ; 
        e[++ cnt] = (E){ x, head[y], 0, -f }, head[y] = cnt ; 
    } 
    queue< int > q; 
    bool spfa() {
        rep( i, s, t ) dis[i] = - inf ; 
        memset( vis, 0, sizeof(vis) ) ;
        q.push(s) ; dis[s] = 0 ;
        while( !q.empty() ) {
            int u = q.front() ; q.pop() ; vis[u] = 0;
            Next( i, u ) {
                int v = e[i].to ; 
                if( dis[v] < dis[u] + e[i].f && e[i].w ) {
                    dis[v] = dis[u] + e[i].f;
                    if( !vis[v] ) q.push(v), vis[v] = 1; 
                }    
            }
        }
        return dis[t] != -inf ;
    }
    int dfs( int x, int dist ) {
        mark[x] = 1 ; 
        if( x == t ) return dist ; 
        int flow = 0 ; 
        for( register int &i = cur[x]; i; i = e[i].next ) {
            int v = e[i].to ; 
            if( !mark[v] && dis[v] == dis[x] + e[i].f && e[i].w ) {
                int di = dfs( v, min( dist, e[i].w ) ) ; 
                if( di > 0 ) {
                    e[i].w -= di, e[i ^ 1].w += di;
                    flow += di, dist -= di ; 
                    Ans += di * e[i].f ; 
                    if( dist == 0 ) return flow ; 
                }
            }
        }
        return flow ;
    }
    void zkwflow() {
        Ans = 0;
        while(spfa()) {
            memcpy( cur, head, sizeof(head) ) ;
            mark[t] = 1 ; 
            while( mark[t] ) {
                memset( mark, 0, sizeof(mark) ) ;
                dfs( s, inf ) ;
            }
        }
    } 
    bool check( double x ) {
        s = 0, t = 2 * n + 1 ; cnt = 1;
        memset( head, 0, sizeof(head) );
        rep( i, 1, n ) add( s, i, 1, 0 ), add( i + n, t, 1, 0 ) ;
        rep( i, 1, n ) rep( j, 1, n ) add( i, j + n, 1, a[i][j] - x * b[i][j] );
        zkwflow() ;
        return Ans >= 0 ;
    }
    void solve() {
        double l = 0, r = 10000 + 5, mid, ans = 0 ;
        while( r - l > eps ) {
            mid = ( l + r ) / 2.0 ; 
            if( check(mid) ) ans = mid, l = mid + eps ; 
            else r = mid - eps ;
        }
        printf("%.6lf\n", ans ) ;
    }
    signed main()
    {
        n = read() ; 
        rep( i, 1, n ) rep( j, 1, n ) a[i][j] = read() ; 
        rep( i, 1, n ) rep( j, 1, n ) b[i][j] = read() ; 
        solve() ; 
        return 0;
    }
    View Code
    这道题主要是要发现出点和入点的关系,之后需要注意的点就是,这个方向是可以出图的,就是不用判边界,而是要取模。
    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    typedef pair<ll, int> pii;
    
    struct Edge {
        int u, v;
        ll flow, cap, cost;
        int next;
    };
    
    const int MAXN = 5000+10, MAXM = 50000;
    const ll LLINF = 0x3f3f3f3f3f3f3f3fLL;
    int n, m, k;
    int e_ptr = 1, S, T, head[MAXN]; Edge E[(MAXM)<<1];
    ll dist[MAXN], MaxFlow, MinCost, delta;
    int inq[MAXN], done[MAXN], vis[MAXN];
    
    void AddEdge(int u, int v, ll cap, ll cost) {
        E[++e_ptr] = (Edge) { u, v, 0, cap, cost, head[u] }; head[u] = e_ptr;
        E[++e_ptr] = (Edge) { v, u, 0,  0, -cost, head[v] }; head[v] = e_ptr;
    }
    
    void Reduce() {
        for(int i = 2; i <= e_ptr; i++)
            E[i].cost += dist[E[i].v] - dist[E[i].u];
        delta += dist[S];
    }
    
    bool BellmanFord() {
        queue<int> Q;
        memset(dist, 0x3f, sizeof(dist));
        dist[T] = 0; Q.push(T); inq[T] = true;
        while(!Q.empty()) {
            int u = Q.front(); Q.pop(); inq[u] = false;
            for(int j=head[u]; j; j=E[j].next) {
                int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost;
                if(f < c && dist[v] > dist[u] + len) {
                    dist[v] = dist[u] + len;
                    if(!inq[v]) {
                        inq[v] = true;
                        Q.push(v);
                    }
                }
            }
        }
        return dist[S] != LLINF;
    }
    
    bool Dijkstra() {
        memset(dist, 0x3f, sizeof(dist));
        memset(vis, 0, sizeof(vis));
        priority_queue<pii,vector<pii>,greater<pii> > pq;
        dist[T] = 0; pq.push({dist[T], T});
        while(!pq.empty()) {
            int u = pq.top().second; pq.pop();
            if(vis[u]) continue;
            vis[u] = 1;
            for(int j=head[u]; j; j=E[j].next) {
                int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost;
                if(f < c && dist[v] > dist[u] + len) {
                    dist[v] = dist[u] + len;
                    pq.push({dist[v],v});
                }
            }
        }
        return dist[S] != LLINF;
    }
    
    ll DFS(int u, ll flow) {
        if(u == T || flow == 0) return flow;
        vis[u] = true; // differ from dinic
        ll res = flow;
        for(int j=head[u]; j; j=E[j].next) {
            int v = E[j].v; ll f = E[j].flow, c = E[j].cap, len = E[j].cost;
            if(!vis[v] && f < c && len == 0) { // not `dist[v] == dist[u]` ! they do not equal !
                ll tmp = DFS(v, min(res, c-f)); // len = 0 <=> on the shortest path
                E[j].flow += tmp;
                E[j^1].flow -= tmp;
                res -= tmp;
            }
        }
        return flow - res;
    }
    
    void Augment() {
        ll CurFlow = 0;
        while(memset(vis, 0, sizeof(vis)),
            (CurFlow = DFS(S, LLINF))) {
            MaxFlow += CurFlow;
            MinCost += CurFlow * delta;
        }
    }
    
    void PrimalDual() {
        if(!BellmanFord()) return;
        Reduce(); Augment();
        while(Dijkstra()) {
            Reduce(); Augment();
        }
    }
    
    map<char, int> DicHash;
    int g[20][20];
    int dic[4][2] = {-1,0, 1,0, 0,-1, 0,1};
    
    int main() {
        DicHash['U'] = 0, DicHash['D'] = 1;
        DicHash['L'] = 2, DicHash['R'] = 3;
        cin >> n >> m;
        S = 0, T = n*m*2+1;
        for (int i = 1; i <= n; ++ i) {
            for (int j = 1; j <= m; ++ j) {
                char ch; cin >> ch, g[i][j] = DicHash[ch];
                int id = (i-1)*m+j;
                AddEdge(S, id, 1, 0), AddEdge(id+n*m, T, 1, 0);
                for (int k = 0; k < 4; ++ k) {
                    int tx = (i+dic[k][0]+n-1)%n+1, ty = (dic[k][1]+j+m-1)%m+1;
                    int _id = (tx-1)*m+ty;
                    AddEdge(id, _id+n*m, 1, g[i][j] != k);
                }
            }
        }
        PrimalDual();
        cout << MinCost << endl;
        return 0;
    }
    View Code
  • 相关阅读:
    时间随手记
    laravel简书(2)
    laravel简书(1)
    <<Design Patterns>> Gang of Four
    计算机网络你还懵逼吗?持续更新!!!
    poj3126 搜索
    POJ 1426 搜索进阶
    CodeForces 660D
    poj3279搜索详解
    bfs简单题-poj2251
  • 原文地址:https://www.cnblogs.com/Vikyanite/p/13588605.html
Copyright © 2011-2022 走看看