UESTC Training for Graph Theory 网络流入门 算法竞赛入门经典 矩阵解压 Matrix Decompressing 改编

C - 天才钱vs学霸周2   

大概题意:给定一个矩阵 第i 行的和 ai ,以及第 j 列的和bj ,求问能否找出满足条件的一个矩阵。

思路分析:题目说“暴力都可以啊”,我感觉这是在骗老实人。如果直接暴力的话,500ms 应该是过不了的,那么又该如何去解决呢?还记得紫书上面的一道题吗?网络流那一章的一道例题:矩阵解压。然而题目所给的是前 i 行之和ai ,以及前 j 列之和bj ,当然这都不是问题,而我们的题目又要判断是否该矩阵存在。 当然原来的题目保证有解,那么我们这个题目就不一样了,还需要判断是否有解。什么情况下有解呢?很明显是满流的情况下,因为这些数值都是已经给定的。对于构图,先设置源点和汇点,将每行之和 ai 看成一个点,每列之和 bj 看成一个节点,然后对图跑一遍最大流即可。下面看具体代码实现:

核心代码:
1.

首先看一下主程序的处理。由于网络流可以是0 流,所以不妨将每一行,每一列的元素都减一,那么范围就是 0~19 了,满足网络流的基本条件,然后便是在源点与 c[ i ], 汇点与 d[ j ], c[ i ]与d[ j ]中间建边。
2.

初始化。这里的cap 是边的容量,就是可以最大流经的流量,而flow 则是实际流经的流量。
3.

然后就是dinic 跑最大流。其实紫书上是给了另一种算法,但我看网上的资料一般大多数是 dinic ,所以就以 dinic 讲解。注意这里的 Addedge ( int from, int to, int cap),我们不仅要建一条流向下一个节点的边,还要建一条反边,换句话说,可以有“反悔”的选择。
4.

然后就是用 bfs 建立分层图了,首先初始化不用多说,当我该顶点未被访问 && 容量>流量时,深度加1 ,然后入队列;最后返回汇点t 的访问情况。
5.

接着 dfs 记录最大流。这里使用了当前弧优化。如果到了汇点,那么直接return ;当前弧优化的意思就是不必每次都从第一个开始跑而是从上一个点i 遍历跑到的点开始。其中正边每次加上 f ,而反边减去 f ,这里的反边用 ^1 即可,因为我们建边都是正边与反边一起建的,如果没有流量了,那么就break ,最后返回 flow 。
6.

判断是否有解。首先判断源点连接的边的流量情况,如果不是满流,那么返回 false ;同样判断看是否满流。如果都是 true ,那么就是有解。
7.

最大流。首先不断的bfs 构建分层图,然后用flow 记录最大流。如果满足满流条件,那么输出流量+1,因为我们之前-1的操作;不然输出NO,即无解。

#include<iostream>
#include<queue>
#include<cstring>
#include<cstdio>
#define maxn 1000
#define INF 0x3f3f3f3f

using namespace std;

struct Edge
{
    int from,to,cap,flow;//   顾名思义 from to 容量 流量
    Edge(int u,int v,int c,int f):
        from(u),to(v),cap(c),flow(f) {}
};

struct Dinic
{
    int n,m,s,t;
    vector<Edge> edges;
    vector<int> G[maxn];
    int d[maxn];
    int cur[maxn];
    bool vis[maxn];

    void init(int 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()
    {
        //  建立分层图
        memset(vis,false,sizeof(vis));
        for (int i=0; i<n; i++) d[i] = INF;
        d[s] = 0; vis[s] = true;

        queue<int> Q;
        Q.push(s);
        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] = true;
                    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[e.to] == d[x]+1 && (f = DFS(e.to,min(a,e.cap-e.flow))) > 0)
            {
                e. flow += f;
                edges[G[x][i]^1].flow -= f;//   反边减去f
                flow += f;
                a -= f;
                if (a == 0)
                    break;
            }
        }
        return flow;
    }

    bool OKA()
    {
        // 判断是否满流
        for (int i=0; i<G[s].size(); i++)
        {
            Edge e = edges[G[s][i]];
            if (e.cap!=e.flow)
                return false;
        }
        return true;
    }

    bool OKB(int R,int C)
    {
        for (int j=R+1; j<=R+C; j++)
        {
            Edge& e = edges[G[j][0]];
            if (e.cap!=e.flow)
                return false;
        }
        return true;
    }


    void Maxflow(int R,int C)
    {
        int flow = 0;
        while (BFS())
        {
            memset(cur,0,sizeof(cur));
            flow += DFS(s,INF);
        }

        if (OKA()&&OKB(R,C) )
        {
            cout<<"Yes"<<endl;
            for (int i=1; i<=R; i++)
            {
                int j;
                for (j=1; j<G[i].size()-1; j++)
                    cout<<edges[G[i][j]].flow+1<<' ';
                cout<<edges[G[i][j]].flow+1<<endl;
            }
        }
        else {
            cout<<"No"<<endl;
        }
        cout<<endl;
    }
};

int main()
{
    Dinic aa;// 统一放在结构体中
    int T,R,C,tmp;
    int a[30],b[30],c[30],d[30];

        aa.init(maxn);
        scanf("%d%d",&R,&C);
        for (int i=1; i<=R; i++) cin>>a[i];
        for (int i=1; i<=C; i++) cin>>b[i];
        for (int i=1; i<=R; i++) c[i] = a[i]-C;
        for (int i=1; i<=C; i++) d[i] = b[i]-R;

        for (int i=1; i<=R; i++)
            aa.Addedge(0,i,c[i]);//  源点s 建边
        for (int i=1; i<=C; i++)
            aa.Addedge(R+i,R+C+1,d[i]);//  汇点 t 建边
        for (int i=1; i<=R; i++)
            for (int j=1; j<=C; j++)
                aa.Addedge(i,R+j,19);
        aa.s = 0; aa.t = R+C+1;
        aa.Maxflow(R,C);

    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40273481/article/details/80852426