日常训练 20170531 矩阵

题意简述:给定一个整数矩阵 A[n×m] ,求一个矩阵 B[n×m] ,满足 1in,1jm,Bi,j[L,R] ,且使下式值最小:

maxmax1jm{i=1n(Ai,jBi,j)}max1inj=1m(Ai,jBi,j)

输出任意一组合法解即可。 n,m200,0LR103,0Ai,j103

这题我考场上就直接弃题了,因为本来是想写随机调整的,结果因为有些细节想做得精细一点,就觉得很麻烦,比如找到瓶颈行或瓶颈列,在上面随机修改一个能改的点,我一直在纠结怎么很快找到一个能改的点,然而XYX就直接遇到不能改的就跳过,随下一个,感觉这样才是随机化的正确姿势嘛。
正解是网络流,是经典的矩阵的建图,左侧一个点代表一行,右侧一个点代表一列,点上的限制放在左右点的连边的流量限制上,二分答案,然后就有了行和列的限制,放在右侧点与汇点的连边上。
Richard Peng今天也讲到网络流也像是随机调整。

网络流:

#include<bits/stdc++.h>
const int N = 205;
const int P = N * 2;
const int M = N * N * 4;
const int INF = 1e9;
template <typename T> void read(T &x) {
    x = 0; char c = getchar();
    for (; !isdigit(c); c = getchar());
    for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
}
int n, m, a[N][N], b[N][N], sa[N], sb[N], L, R, S, T, source, sink, in[P], out[P], first[P], cur[P], q[P], h[P], s;
struct edge {
    int y, v, next;
}mp[M];
void ins(int x, int y, int v) {
    mp[++s] = (edge) {y, v, first[x]}; first[x] = s;
    mp[++s] = (edge) {x, 0, first[y]}; first[y] = s;
}
void insert(int x, int y, int L, int R) {
    ins(x, y, R-L);
    in[y] += L;
    out[x] += L;
}
void build(int x){
    memset(first, 0, sizeof(first)); s = 1;
    memset(in, 0, sizeof(in));
    memset(out, 0, sizeof(out));
    for (int i=1; i <= n; i++)
        insert(source, i, std::max(0, sa[i] - x), sa[i] + x);
    for (int j=1; j <= m; j++)
        insert(n + j, sink, std::max(0, sb[j] - x), sb[j] + x);
    for (int i=1; i <= n; i++)
        for (int j=1; j <= m; j++)
            insert(i, n+j, L, R);
    for (int i=S; i <= sink; i++)
        if (in[i] > out[i])
            ins(S, i, in[i] - out[i]);
        else
            ins(i, T, out[i] - in[i]);
    ins(sink, source, INF);
}
bool bfs() {
    memset(h, 0, sizeof(h));
    int head = 1, tail = 2;
    h[q[head] = S] = 1;
    for (int i=0; i <= sink; i++)
        cur[i] = first[i];
    while (head != tail) {
        int x = q[head++];
        for (int t=first[x]; t; t=mp[t].next)
            if (mp[t].v && !h[mp[t].y]){
                h[mp[t].y] = h[x] + 1;
                q[tail++] = mp[t].y;
                if (mp[t].y == T) return 1;
            }
    }
    return 0;
}
int dfs(int x, int f) {
    if (x == T) return f;
    int used = 0, b;
    for (int t=cur[x]; t; t=cur[x]=mp[t].next)
        if (h[mp[t].y] == h[x] + 1) {
            b = dfs(mp[t].y, std::min(f-used, mp[t].v));
            mp[t].v -= b;
            mp[t^1].v += b;
            used += b;
            if (used == f) return used;
        }
    h[x] = -1;
    return used;
}
bool check(int x) {
    build(x);
    while (bfs()) dfs(S,INF);
    for (int t=first[S]; t; t=mp[t].next)
        if (mp[t].v)
            return 0;
    return 1;
}
int main(){
    read(n);read(m);
    for (int i=1; i <= n; i++)
        for (int j=1; j <= m; j++)
            read(a[i][j]),
            sa[i] += a[i][j],
            sb[j] += a[i][j];
    read(L);read(R);
    S = 0; T = n + m + 1;
    source = n + m + 2; sink = n + m + 3;
    int l = 0, r = (int)2.1e5;
    while (l < r) {
        int mid = (l + r) / 2;
        if (check(mid))
            r = mid;
        else
            l = mid + 1;
    }
    printf("%d\n",l);
    check(l);
    for (int i=n+1; i <= n+m; i++)
        for (int t=first[i]; t; t=mp[t].next)
            if (mp[t].y >= 1 && mp[t].y <= n)
                b[mp[t].y][i - n] = mp[t].v + L;
    for (int i=1; i <= n; i++) {
        for (int j=1; j <= m; j++)
            printf("%d ",b[i][j]);
        printf("\n");
    }
    return 0;
}

贪心:

#include<bits/stdc++.h>
const int N = 205;
const int INF = 2e6;
int n, m, L, R, a[N][N], b[N][N], sa[N*2], sb[N*2], max_det, c1, c2, r;
template <typename T> void read(T &x) {
    x = 0; char c = getchar();
    for (; !isdigit(c); c = getchar());
    for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
}
void calc_det() {
    max_det = -1;
    for (int i=1; i <= n + m; i++)
        if (abs(sa[i] - sb[i]) > max_det)
            max_det = abs(sa[i] - sb[i]);
}
int qry(int x, int y, int c) {
    if (b[x][y] + c < L || b[x][y] + c > R) return INF;
    return std::max(abs(sb[x] + c - sa[x]), abs(sb[n + y] + c - sa[n + y]));
}
void chg(int x, int y, int c) {
    b[x][y] += c;
    sb[x] += c;
    sb[n + y] += c;
}
int main() {
    srand(time(0));
    read(n);read(m);
    for (int i=1; i <= n; i++)
        for (int j=1; j <= m; j++)
            read(a[i][j]),
            sa[i] += a[i][j],
            sa[n + j] += a[i][j];
    read(L);read(R);
    for (int i=1; i <= n; i++)
        for (int j=1; j <= m; j++)
            b[i][j] = std::max(std::min(a[i][j], R), L),
            sb[i] += b[i][j],
            sb[n + j] += b[i][j];
    for (int T=(int)1e7/n; T; T--) {
        calc_det();
        if (!max_det) break;
        for (int i=1; i <= n; i++)
            if (abs(sa[i] - sb[i]) == max_det) {
                c1 = qry(i, r = rand() % m + 1, 1);
                c2 = qry(i, r, -1);
                if (std::min(c1, c2) > max_det) continue;
                if (c1 < c2)
                    chg(i, r, 1);
                else
                    chg(i, r, -1);
            }
        for (int j=1; j <= m; j++)
            if (abs(sa[n + j] - sb[n + j]) == max_det) {
                c1 = qry(r = rand() % n + 1, j, 1);
                c2 = qry(r, j, -1);
                if (std::min(c1, c2) > max_det) continue;
                if (c1 < c2)
                    chg(r, j, 1);
                else
                    chg(r, j, -1);
            }
    }
    calc_det();
    printf("%d\n",max_det);
    for (int i=1; i <= n; i++) {
        for (int j=1; j <= m; j++)
            printf("%d ",b[i][j]);
        printf("\n");
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值