题意简述:给定一个整数矩阵 A[n×m] ,求一个矩阵 B[n×m] ,满足 ∀1≤i≤n,1≤j≤m,Bi,j∈[L,R] ,且使下式值最小:
max⎧⎩⎨⎪⎪⎪⎪max1≤j≤m{∣∣∣∣∑i=1n(Ai,j−Bi,j)∣∣∣∣}max1≤i≤n⎧⎩⎨⎪⎪∣∣∣∣∑j=1m(Ai,j−Bi,j)∣∣∣∣⎫⎭⎬⎪⎪⎫⎭⎬⎪⎪⎪⎪
输出任意一组合法解即可。 n,m≤200,0≤L≤R≤103,0≤Ai,j≤103
这题我考场上就直接弃题了,因为本来是想写随机调整的,结果因为有些细节想做得精细一点,就觉得很麻烦,比如找到瓶颈行或瓶颈列,在上面随机修改一个能改的点,我一直在纠结怎么很快找到一个能改的点,然而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;
}