Uvalive 4865 Data Recovery 最大流

题意就是

给一个50 * 50的矩阵

然后给出每行每列元素的和

和一个初始矩阵

矩阵中有些是未知,有些是已知

然后我们求目标矩阵就是把能确定的元素的值求出来,实在不能确定的就置为-1

所有矩阵元素的值在0-100之间


看到范围很小。

第一反应是求一个最大流

先把已经给出的元素都从每行每列的和中减掉。

然后左边为行结点,右边为列结点

然后源点向行结点连边

列结点向汇点连边

行和列中如果对应的元素未知就连一下,流向上限是100

然后这样我们就得到了一个可行解

但是可能有多解怎么办

对于一个可能多解的元素

如果我们将这个元素的值固定住。

然后建立一个超级源点与该行结点连边。

该列结点与超级汇点连边。

流量都是1,

跑一遍看看有没有增广路。

如果有,显然这个位置的值是可以改变的,就是多解,然后我们把这个位置的元素值-1,因为我们刚才增广了,其他有元素的值增加了1,所以

为了保持流量的平衡,这个位置的元素要减1

但是这样还不行。

我们想想。

如果该位置的值现在是0怎么办。

他没法减掉1。

所以我们就要想想残余网络了。

既然他没法减掉1,就让他想办法+1,让别的元素-1去

那么我们可以用一个超级源点连接列结点。

行结点连接超级汇点

跑最大流,看有没有增广路。

也就是看他的残余网络能不能减掉1.即它自身+1

如果有增广路,跟之前一样,更新一下边


#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#define MAXN 106
#define MAXM 211111
#define INF 1111111111
using namespace std;
struct EDGE
{
    int v, next;
    int w;
}edge[MAXM];
int head[MAXN], e;
void init()
{
    memset(head, -1, sizeof(head));
    e = 0;
}
void add(int u, int v, int w)
{
    edge[e].v = v;
    edge[e].w = w;
    edge[e].next = head[u];
    head[u] = e++;
    edge[e].v = u;
    edge[e].w = 0;
    edge[e].next = head[v];
    head[v] = e++;
}
int n;
int h[MAXN];
int gap[MAXN];
int src, des;
int tt[111][111];
int dfs(int pos, int cost)
{
    if(pos == des) return cost;
    int j, minh = n - 1;
    int lv = cost, d;
    for(j = head[pos]; j != -1; j = edge[j].next)
    {
        int v = edge[j].v;
        int w = edge[j].w;
        if(w > 0)
        {
            if(h[v] + 1 == h[pos])
            {
                if(lv < edge[j].w) d = lv;
                else d = edge[j].w;
                d = dfs(v, d);
                edge[j].w -= d;
                edge[j ^ 1].w += d;
                lv -= d;
                if(h[src] >= n) return cost - lv;
                if(lv == 0) break;
            }
            if(h[v] < minh) minh = h[v];
        }
    }
    if(lv == cost)
    {
        --gap[h[pos]];
        if(gap[h[pos]] == 0) h[src] = n;
        h[pos] = minh + 1;
        ++gap[h[pos]];
    }
    return cost - lv;
}
int sap()
{
    int ret = 0;
    memset(gap, 0, sizeof(gap));
    memset(h, 0, sizeof(h));
    gap[0] = n;
    while(h[src] < n) ret += dfs(src, INF);
    return ret;
}
int nt, m;
int col[55], row[55];
int val[55][55], id[55][55];
int ans[55][55];
int vis[55][55];

int main()
{
    //freopen("C:/C.in", "r", stdin);
    //freopen("C:/C2.out", "w", stdout);
    while(scanf("%d%d", &nt, &m) != EOF)
    {
        if(!nt && !m) break;
        for(int i = 1; i <= nt; i++)
            for(int j = 1; j <= m; j++)
                scanf("%d", &val[i][j]);
        for(int i = 1; i <= nt; i++) scanf("%d", &row[i]);
        for(int i = 1; i <= m; i++) scanf("%d", &col[i]);
        memset(ans, -1, sizeof(ans));
        for(int i = 1; i <= nt; i++)
            for(int j = 1; j <= m; j++)
            {
                if(val[i][j] != -1)
                {
                    row[i] -= val[i][j];
                    col[j] -= val[i][j];
                    ans[i][j] = val[i][j];
                }
            }

        init();
        src = nt + m + 1;
        des = nt + m + 2;
        n = des;
        int S = nt + m + 3;
        int T = nt + m + 4;

        for(int i = 1; i <= nt; i++)
            for(int j = 1; j <= m; j++)
            {
                if(ans[i][j] == -1)
                {
                    id[i][j] = e;
                    add(i, j + nt, 100);
                }
            }
        for(int i = 1; i <= nt; i++)
        {
            add(src, i, row[i]);
        }
        for(int j = 1; j <= m; j++)
        {
            add(j + nt, des, col[j]);
        }
        sap();
        n = T;
        memset(vis, 0, sizeof(vis));
        for(int i = 1; i <= nt; i++)
            for(int j = 1; j <= m; j++)
                if(ans[i][j] != -1) vis[i][j] = 2;
        src = S;
        des = T;
        for(int i = 1; i <= nt; i++)
        {
            for(int j = 1; j <= m; j++)
            {
                if(vis[i][j] == 2) continue;
                int te = id[i][j];
                int tmp = edge[te ^ 1].w;
                edge[te].w = edge[te ^ 1].w = 0;
                int flag = 1;
                int le = e;
                add(src, i, 1);
                int me = e;
                add(j + nt, des, 1);
                if( tmp && sap()) flag = 0, tmp--;
                edge[le].w = edge[le ^ 1].w = 0;
                edge[me].w = edge[me ^ 1].w = 0;
                le = e;
                add(src, j + nt, 1);
                me = e;
                add(i, des, 1);
                if(flag  && 100 - tmp > 0&& sap()) flag = 0, tmp++;
                edge[le].w = edge[le ^ 1].w = 0;
                edge[me].w = edge[me ^ 1].w = 0;

                edge[te ^ 1].w = tmp;
                edge[te].w = 100 - tmp;
                vis[i][j] = flag;
            }

        }
        for(int i = 1; i <= nt; i++)
            for(int j = 1; j <= m; j++)
            {
                if(vis[i][j] == 2)
                {
                    if(ans[i][j] != -1) printf("%d", ans[i][j]);
                }
                else
                {
                    if(vis[i][j]) printf("%d", edge[id[i][j] ^ 1].w);
                    else printf("-1");
                }
                if(j < m) printf(" ");
                else printf("\n");
            }

    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值